/* Copyright (c) 2017-2025 Ahmed Kh. Zamil Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ using Esiur.Core; using Esiur.Data; using Esiur.Data.Types; using Esiur.Misc; using Esiur.Net; using Esiur.Net.Http; using Esiur.Net.Packets; using Esiur.Net.Packets.Http; using Esiur.Net.Sockets; using Esiur.Resource; using Esiur.Security.Authority; using Esiur.Security.Cryptography; using Esiur.Security.Membership; using Esiur.Security.Permissions; using Microsoft.CodeAnalysis.CSharp.Syntax; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Net; using System.Net.Sockets; using System.Reflection.PortableExecutable; using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using System.Timers; namespace Esiur.Protocol; public partial class EpConnection : NetworkConnection, IStore { public delegate void ProtocolGeneralHandler(EpConnection connection, ParsedTdu dataType, byte[] data); public delegate void ProtocolRequestReplyHandler(EpConnection connection, uint callbackId, ParsedTdu dataType, byte[] data); // Delegates public delegate void ReadyEvent(EpConnection sender); public delegate void ErrorEvent(EpConnection sender, byte errorCode, string errorMessage); public delegate void ResumedEvent(EpConnection sender); // Events /// /// Ready event is raised when autoReconnect is enabled and the connection is restored. /// public event ResumedEvent OnResumed; /// /// Ready event is raised when the connection is fully established. /// public event ReadyEvent OnReady; /// /// Error event /// public event ErrorEvent OnError; // Fields bool _invalidCredentials = false; System.Timers.Timer _keepAliveTimer; DateTime? _lastKeepAliveSent; DateTime? _lastKeepAliveReceived; EpPacket _packet; //= new EpPacket(); EpAuthPacket _authPacket;// = new EpAuthPacket(); Session _session; AsyncReply _openReply; bool _authenticated; string _hostname; ushort _port; bool _initialPacket = true; AuthenticationDirection _authDirection = AuthenticationDirection.Responder; Map _remoteTypeDefs = new Map(); // Properties public DateTime LoginDate { get; private set; } /// /// Distributed server responsible for this connection, usually for incoming connections. /// /// EpServer _server; Warehouse _serverWarehouse; public EpServer Server => _server; //public EpServer Server //{ // get => _server; // internal set // { // _server = value; // if (_authPacket == null) // _authPacket = new EpAuthPacket(value.Instance.Warehouse); // if (_packet == null) // _packet = new EpPacket(value.Instance.Warehouse); // } //} /// /// The session related to this connection. /// public Session Session => _session; [Export] public virtual EpConnectionStatus Status { get; private set; } [Export] public virtual uint Jitter { get; private set; } // Attributes //[Attribute] public uint KeepAliveTime { get; set; } = 10; //[Attribute] public ExceptionLevel ExceptionLevel { get; set; } = ExceptionLevel.Code | ExceptionLevel.Message | ExceptionLevel.Source | ExceptionLevel.Trace; //[Attribute] public bool AutoReconnect { get; set; } = false; //[Attribute] public uint ReconnectInterval { get; set; } = 5; //[Attribute] //public string Username { get; set; } //[Attribute] public bool UseWebSocket { get; set; } //[Attribute] public bool SecureWebSocket { get; set; } //[Attribute] //public string Password { get; set; } //[Attribute] //public string Token { get; set; } //[Attribute] //public ulong TokenIndex { get; set; } //[Attribute] //public string Domain { get; set; } string _remoteDomain, _localDomain; public string RemoteDomain { get => _remoteDomain; set { _remoteDomain = value; _session.RemoteHeaders[EpAuthPacketHeader.Domain] = value; } } public string LocalDomain { get => _localDomain; set { _localDomain = value; _session.LocalHeaders[EpAuthPacketHeader.Domain] = value; } } public bool Remove(IResource resource) { // nothing to do return true; } /// /// Send data to the other end as parameters /// /// Values will be converted to bytes then sent. internal SendList SendParams(AsyncReply reply = null) { return new SendList(this, reply); } /// /// Send raw data through the connection. /// /// Data to send. public override void Send(byte[] data) { #if VERBOSE Console.WriteLine("Client: {0}", data.Length); #endif Global.Counters["Ep Sent Packets"]++; base.Send(data); } /// /// KeyList to store user variables related to this connection. /// public KeyList Variables { get; } = new KeyList(); /// /// IResource interface. /// public Instance Instance { get; set; } /// /// Assign a socket to the connection. /// /// Any socket that implements ISocket. public override void Assign(ISocket socket) { base.Assign(socket); _session.LocalHeaders[EpAuthPacketHeader.IPAddress] = socket.RemoteEndPoint.Address.GetAddressBytes(); if (socket.State == SocketState.Established && _authDirection == AuthenticationDirection.Initiator) { Declare(); } } private void Declare() { if (_authDirection != AuthenticationDirection.Initiator) return; // change to Map for compatibility var headers = _session.LocalHeaders.Select(x => new KeyValuePair((byte)x.Key, x.Value)); if (_session.AuthenticationMode != AuthenticationMode.None) { if (_session.AuthenticationHandler == null) throw new Exception("Authentication handler must be assigned for the session."); var initAuthResult = _session.AuthenticationHandler.Process(null); headers.Add((byte)EpAuthPacketHeader.AuthenticationProtocol, _session.AuthenticationHandler.Protocol); headers.Add((byte)EpAuthPacketHeader.AuthenticationData, initAuthResult.AuthenticationData); headers.Add((byte)EpAuthPacketHeader.Domain, _remoteDomain); } if (_session.EncryptionMode != EncryptionMode.None) { //@TODO: get the handler } SendAuthHeaders((EpAuthPacketMethod)( (byte)EpAuthPacketMethod.Initialize | (byte)(_session.AuthenticationMode) << 2 | (byte)_session.EncryptionMode), headers); } /// /// Create a new distributed connection. /// /// Socket to transfer data through. /// Working domain. /// Username. /// Password. public EpConnection(ISocket socket, IAuthenticationHandler authenticationHandler, Map headers) { _session = new Session(); //if (authenticationHandler.Type != AuthenticationType.Initiator) // throw new Exception("" //session.AuthenticationType = AuthenticationMode.Initiator; _session.LocalHeaders = headers; if (authenticationHandler != null) { _session.AuthenticationHandler = authenticationHandler; //session.AuthenticationMode = authenticationHandler.Mode; } //this.localPasswordOrToken = DC.ToBytes(password); init(); Assign(socket); } //public EpConnection(ISocket socket, string domain, ulong tokenIndex, string token) //{ // this.session = new Session(); // session.AuthenticationType = AuthenticationType.Client; // session.LocalHeaders[EpAuthPacketHeader.Domain] = domain; // session.LocalHeaders[EpAuthPacketHeader.TokenIndex] = tokenIndex; // session.LocalMethod = AuthenticationMethod.Credentials; // session.RemoteMethod = AuthenticationMethod.None; // this.localPasswordOrToken = DC.ToBytes(token); // init(); // Assign(socket); //} /// /// Create a new instance of a distributed connection /// public EpConnection() { _session = new Session(); //session.AuthenticationType = AuthenticationMode.Responder; //session.AuthenticationResponder = authenticationResponder; //authenticationResponder.Initiate(session); init(); } public string Link(IResource resource) { if (resource is EpResource) { var r = resource as EpResource; if (r.Instance.Store == this) return this.Instance.Name + "/" + r.ResourceInstanceId; } return null; } /// /// Enables or disables retaining delivered resource queue items for diagnostics. /// Capture is disabled by default. /// public void SetFinishedQueueCapture(bool enabled) { _queue.SetProcessedCapture(enabled); } /// /// Atomically returns and removes the retained delivered resource queue items. /// public List> GetFinishedQueue() { return _queue.DrainProcessed(); } void init() { //var q = queue; _queue.Then((x) => { if (x.Type == EpResourceQueueItem.DistributedResourceQueueItemType.Event) x.Resource._EmitEventByIndex(x.Index, x.Value); else x.Resource._UpdatePropertyByIndex(x.Index, x.Value); }).Error(e => { // do nothing //Console.WriteLine("Queue is empty"); throw e; }); // set local nonce //session.LocalHeaders[EpAuthPacketHeader.Nonce] = Global.GenerateBytes(32); _keepAliveTimer = new System.Timers.Timer(KeepAliveInterval * 1000); _keepAliveTimer.Elapsed += KeepAliveTimer_Elapsed; ; } private void KeepAliveTimer_Elapsed(object? sender, ElapsedEventArgs e) { if (!IsConnected) return; _keepAliveTimer.Stop(); var now = DateTime.UtcNow; uint interval = _lastKeepAliveSent == null ? 0 : (uint)(now - (DateTime)_lastKeepAliveSent).TotalMilliseconds; _lastKeepAliveSent = now; SendRequest(EpPacketRequest.KeepAlive, now, interval) .Then(x => { Jitter = Convert.ToUInt32(((object[])x)[1]); _keepAliveTimer.Start(); }).Error(ex => { _keepAliveTimer.Stop(); Close(); }).Timeout((int)(KeepAliveTime * 1000), () => { _keepAliveTimer.Stop(); Close(); }); } public uint KeepAliveInterval { get; set; } = 30; public override void Destroy() { UnsubscribeAll(); this.OnReady = null; this.OnError = null; base.Destroy(); } private uint processPacket(byte[] msg, uint offset, uint ends, NetworkBuffer data, int chunkId) { if (_authenticated) { var rt = _packet.Parse(msg, offset, ends); if (rt <= 0) { var size = ends - offset; data.HoldFor(msg, offset, size, size + (uint)(-rt)); return ends; } else { offset += (uint)rt; if (_packet.Tdu == null) return offset; #if VERBOSE Console.WriteLine("Incoming: " + _packet + " " + _packet.CallbackId); #endif if (_packet.Method == EpPacketMethod.Notification) { var dt = _packet.Tdu.Value; switch (_packet.Notification) { // Invoke case EpPacketNotification.PropertyModified: EpNotificationPropertyModified(dt); break; case EpPacketNotification.EventOccurred: EpNotificationEventOccurred(dt); break; // Manage case EpPacketNotification.ResourceDestroyed: EpNotificationResourceDestroyed(dt); break; case EpPacketNotification.ResourceReassigned: EpNotificationResourceReassigned(dt); break; case EpPacketNotification.ResourceMoved: EpNotificationResourceMoved(dt); break; case EpPacketNotification.SystemFailure: EpNotificationSystemFailure(dt); break; } } else if (_packet.Method == EpPacketMethod.Request) { var dt = _packet.Tdu.Value; switch (_packet.Request) { // Invoke case EpPacketRequest.InvokeFunction: EpRequestInvokeFunction(_packet.CallbackId, dt); break; case EpPacketRequest.SetProperty: EpRequestSetProperty(_packet.CallbackId, dt); break; case EpPacketRequest.Subscribe: EpRequestSubscribe(_packet.CallbackId, dt); break; case EpPacketRequest.Unsubscribe: EpRequestUnsubscribe(_packet.CallbackId, dt); break; // Inquire case EpPacketRequest.TypeDefIdsByNames: EpRequestTypeDefIdsByNames(_packet.CallbackId, dt); break; case EpPacketRequest.TypeDefById: EpRequestTypeDefById(_packet.CallbackId, dt); break; case EpPacketRequest.TypeDefByResourceId: EpRequestTypeDefByResourceId(_packet.CallbackId, dt); break; case EpPacketRequest.Query: EpRequestQueryResources(_packet.CallbackId, dt); break; case EpPacketRequest.LinkTypeDefs: EpRequestLinkTypeDefs(_packet.CallbackId, dt); break; case EpPacketRequest.Token: EpRequestToken(_packet.CallbackId, dt); break; case EpPacketRequest.GetResourceIdByLink: EpRequestGetResourceIdByLink(_packet.CallbackId, dt); break; // Manage case EpPacketRequest.AttachResource: EpRequestAttachResource(_packet.CallbackId, dt); break; case EpPacketRequest.ReattachResource: EpRequestReattachResource(_packet.CallbackId, dt); break; case EpPacketRequest.DetachResource: EpRequestDetachResource(_packet.CallbackId, dt); break; case EpPacketRequest.CreateResource: EpRequestCreateResource(_packet.CallbackId, dt); break; case EpPacketRequest.DeleteResource: EpRequestDeleteResource(_packet.CallbackId, dt); break; case EpPacketRequest.MoveResource: EpRequestMoveResource(_packet.CallbackId, dt); break; // Static case EpPacketRequest.KeepAlive: EpRequestKeepAlive(_packet.CallbackId, dt); break; case EpPacketRequest.ProcedureCall: EpRequestProcedureCall(_packet.CallbackId, dt); break; case EpPacketRequest.StaticCall: EpRequestStaticCall(_packet.CallbackId, dt); break; } } else if (_packet.Method == EpPacketMethod.Reply) { var dt = _packet.Tdu.Value; switch (_packet.Reply) { case EpPacketReply.Completed: EpReplyCompleted(_packet.CallbackId, dt); break; case EpPacketReply.Propagated: EpReplyPropagated(_packet.CallbackId, dt); break; case EpPacketReply.PermissionError: EpReplyError(_packet.CallbackId, dt, ErrorType.Management); break; case EpPacketReply.ExecutionError: EpReplyError(_packet.CallbackId, dt, ErrorType.Exception); break; case EpPacketReply.Progress: EpReplyProgress(_packet.CallbackId, dt); break; case EpPacketReply.Chunk: EpReplyChunk(_packet.CallbackId, dt); break; case EpPacketReply.Warning: EpReplyWarning(_packet.Extension, dt); break; } } else if (_packet.Method == EpPacketMethod.Extension) { EpExtensionAction(_packet.Extension, _packet.Tdu); } } } else { // check if the request through Websockets if (_initialPacket) { _initialPacket = false; if (msg.Length > 3 && Encoding.Default.GetString(msg, 0, 3) == "GET") { // Parse with http packet var req = new HttpRequestPacket(); var pSize = req.Parse(msg, 0, (uint)msg.Length); if (pSize > 0) { // check for WS upgrade if (HttpConnection.IsWebsocketRequest(req)) { Socket?.Unhold(); var res = new HttpResponsePacket(); HttpConnection.Upgrade(req, res); res.Compose(HttpComposeOption.AllCalculateLength); Send(res.Data); // replace my socket with websockets var tcpSocket = this.Unassign(); var wsSocket = new WSocket(tcpSocket); this.Assign(wsSocket); } else { var res = new HttpResponsePacket(); res.Number = HttpResponseCode.BadRequest; res.Compose(HttpComposeOption.AllCalculateLength); Send(res.Data); //@TODO: kill the connection } } else { // packet incomplete return (uint)pSize; } // switching completed return (uint)msg.Length; } } var rt = _authPacket.Parse(msg, offset, ends); if (rt <= 0) { data.HoldFor(msg, ends + (uint)(-rt)); return ends; } else { offset += (uint)rt; #if VERBOSE Console.WriteLine($"AuthPacket: RT: {rt} {authPacket.ToString()}"); #endif if (_authPacket.Command == EpAuthPacketCommand.Initialize && _authDirection == AuthenticationDirection.Initiator) throw new Exception("Bad authentication packet received. Connection is initiator but received an initialization packet."); if (_authPacket.Command == EpAuthPacketCommand.Acknowledge && _authDirection == AuthenticationDirection.Responder) throw new Exception("Bad authentication packet received. Connection is responder but received an acknowledge packet."); if (_authPacket.Command == EpAuthPacketCommand.Initialize) { var remoteHeaders = new Map(); object remoteAuthData = null; if (_authPacket.Tdu != null) { var parsed = Codec.ParseSync(_authPacket.Tdu.Value, null); if (parsed is Map headers) { remoteHeaders = headers; foreach (var header in headers) { if (header.Key == (byte)EpAuthPacketHeader.AuthenticationData) remoteAuthData = header.Value; else _session.RemoteHeaders.Add((EpAuthPacketHeader)header.Key, header.Value); } } } var localHeaders = new Map(); foreach (var header in _session.LocalHeaders) localHeaders.Add((byte)header.Key, header.Value); if (_authPacket.AuthMode == AuthenticationMode.None) { if (!(Server?.AllowUnauthorizedAccess ?? false)) { SendAuthMessage(EpAuthPacketMethod.ErrorTerminate, "Unauthorized access not allowed."); _invalidCredentials = true; //Close(); return offset; } SendAuthHeaders(EpAuthPacketMethod.SessionEstablished, localHeaders); _session.Authenticated = true; _session.LocalIdentity = null; _session.RemoteIdentity = null; AuthenticatonCompleted(); return offset; } if (!_session.RemoteHeaders.ContainsKey(EpAuthPacketHeader.AuthenticationProtocol)) { SendAuthHeaders(EpAuthPacketMethod.NotSupported, localHeaders); _invalidCredentials = true; Close(); return offset; } var provider = _serverWarehouse.GetAuthenticationProvider(_session.RemoteHeaders[EpAuthPacketHeader.AuthenticationProtocol].ToString()); var handler = provider.CreateAuthenticationHandler(new AuthenticationContext() { Direction = AuthenticationDirection.Responder, Mode = _authPacket.AuthMode, Domain = _session.RemoteHeaders.ContainsKey(EpAuthPacketHeader.Domain) ? _session.RemoteHeaders[EpAuthPacketHeader.Domain].ToString() : null, Materials = new AuthenticationMaterial[] { new AuthenticationMaterial() { Type = AuthenticationMaterialType.Data, Value = remoteAuthData } } }); if (handler == null) { SendAuthHeaders(EpAuthPacketMethod.NotSupported, localHeaders); _invalidCredentials = true; Close(); return offset; } // set auth handler for the session _session.AuthenticationHandler = handler; var authResult = handler.Process(remoteAuthData); // send acknowledgements localHeaders.Add((byte)EpAuthPacketHeader.AuthenticationData, authResult.AuthenticationData); if (authResult.Ruling == AuthenticationRuling.Failed) { SendAuthHeaders(EpAuthPacketMethod.Denied, localHeaders); _invalidCredentials = true; Task.Delay(100).ContinueWith(x => Close()); } else if (authResult.Ruling == AuthenticationRuling.InProgress) { SendAuthHeaders(EpAuthPacketMethod.ProceedToHandshake, localHeaders); } else if (authResult.Ruling == AuthenticationRuling.Succeeded) { SendAuthHeaders(EpAuthPacketMethod.SessionEstablished, localHeaders); _session.Authenticated = true; _session.LocalIdentity = authResult.LocalIdentity; _session.RemoteIdentity = authResult.RemoteIdentity; _session.Key = authResult.SessionKey; AuthenticatonCompleted(); } } else if (_authPacket.Command == EpAuthPacketCommand.Acknowledge) { // Anonymous (None-mode) success: the responder establishes the session directly // via SessionEstablished, without a handshake exchange. Complete the connection so // the pending open request resolves. (Previously this was only handled inside the // ProceedToHandshake branch, so a direct SessionEstablished left the initiator hung.) if (_session.AuthenticationMode == AuthenticationMode.None && _authPacket.Method == EpAuthPacketMethod.SessionEstablished) { _session.Authenticated = true; _session.LocalIdentity = null; _session.RemoteIdentity = null; _session.Key = null; AuthenticatonCompleted(); return offset; } if (_authPacket.Method == EpAuthPacketMethod.ProceedToHandshake || _authPacket.Method == EpAuthPacketMethod.ProceedToFinalHandshake) { var remoteHeaders = new Map(); object remoteAuthData = null; if (_authPacket.Tdu != null) { var parsed = Codec.ParseSync(_authPacket.Tdu.Value, Instance.Warehouse); if (parsed is Map headers) { foreach (var header in headers) { if (header.Key == (byte)EpAuthPacketHeader.AuthenticationData) { remoteAuthData = header.Value; } else { remoteHeaders.Add((EpAuthPacketHeader)header.Key, header.Value); } } _session.RemoteHeaders = remoteHeaders;// headers.Select(x => new KeyValuePair((EpAuthPacketHeader)x.Key, x.Value)); } } if (_session.AuthenticationMode == AuthenticationMode.None) { if (_authPacket.Method == EpAuthPacketMethod.SessionEstablished) { _session.Authenticated = true; _session.LocalIdentity = null; _session.RemoteIdentity = null; _session.Key = null; AuthenticatonCompleted(); } else { _invalidCredentials = true; Task.Delay(100).ContinueWith(x => Close()); } return offset; } var authResult = _session.AuthenticationHandler.Process(remoteAuthData); if (authResult.Ruling == AuthenticationRuling.Failed) { SendAuth(EpAuthPacketMethod.ErrorTerminate); _invalidCredentials = true; Task.Delay(100).ContinueWith(x => Close()); return offset; } else if (authResult.Ruling == AuthenticationRuling.InProgress) { if (_authPacket.Method == EpAuthPacketMethod.ProceedToHandshake) { SendAuthData(EpAuthPacketMethod.Handshake, authResult.AuthenticationData); } else { throw new Exception("Bad protocol sequence."); } } else if (authResult.Ruling == AuthenticationRuling.Succeeded) { _session.Authenticated = true; _session.Key = authResult.SessionKey; _session.LocalIdentity = authResult.LocalIdentity; _session.RemoteIdentity = authResult.RemoteIdentity; // send final handshake with data SendAuthData(EpAuthPacketMethod.FinalHandshake, authResult.AuthenticationData); //if (_authPacket.Method == EpAuthPacketMethod.SessionEstablished) //{ // AuthenticatonCompleted(authResult.LocalIdentity, authResult.RemoteIdentity); //} //else if (_authPacket.Method == EpAuthPacketMethod.ProceedToEstablishSession // || _authPacket.Method == EpAuthPacketMethod.FinalHandshake) //{ // // Send establish request // SendAuthData(EpAuthPacketMethod.FinalHandshake, // authResult.AuthenticationData); //} } } else if (_authPacket.Method == EpAuthPacketMethod.Denied) { var errorMessage = "Authentication error."; if (_authPacket.Tdu != null) { var parsed = Codec.ParseSync(_authPacket.Tdu.Value, _serverWarehouse); if (parsed is string parsedErrorMsg) errorMessage = parsedErrorMsg; } _invalidCredentials = true; OnError?.Invoke(this, _authPacket.ErrorCode, errorMessage); _openReply?.TriggerError(new AsyncException(ErrorType.Management, _authPacket.ErrorCode, "Authentication error.")); } } else if (_authPacket.Command == EpAuthPacketCommand.Action) { object authData = null; if (_authPacket.Tdu != null) { var parsed = Codec.ParseSync(_authPacket.Tdu.Value, _serverWarehouse); authData = parsed; } if (_authPacket.Method == EpAuthPacketMethod.Handshake || _authPacket.Method == EpAuthPacketMethod.FinalHandshake) { var authResult = _session.AuthenticationHandler.Process(authData); if (authResult.Ruling == AuthenticationRuling.Failed) { SendAuth(EpAuthPacketMethod.ErrorTerminate); _invalidCredentials = true; Task.Delay(100).ContinueWith(x => Close()); } else if (authResult.Ruling == AuthenticationRuling.InProgress) { SendAuthData(EpAuthPacketMethod.Handshake, authResult.AuthenticationData); } else if (authResult.Ruling == AuthenticationRuling.Succeeded) { _session.Authenticated = true; _session.Key = authResult.SessionKey; _session.LocalIdentity = authResult.LocalIdentity; _session.RemoteIdentity = authResult.RemoteIdentity; if (authResult.AuthenticationData != null) { SendAuthData(EpAuthPacketMethod.FinalHandshake, authResult.AuthenticationData); } if (_authDirection == AuthenticationDirection.Responder && _authPacket.Method == EpAuthPacketMethod.FinalHandshake) { // Send established event SendAuth(EpAuthPacketMethod.Established); AuthenticatonCompleted(); } } } } else if (_authPacket.Command == EpAuthPacketCommand.Event) { if (_authPacket.Method == EpAuthPacketMethod.ErrorTerminate || _authPacket.Method == EpAuthPacketMethod.ErrorMustEncrypt || _authPacket.Method == EpAuthPacketMethod.ErrorRetry) { var errorMessage = "Authentication error."; if (_authPacket.Tdu != null) { var parsed = Codec.ParseSync(_authPacket.Tdu.Value, _serverWarehouse); if (parsed is string parsedErrorMsg) errorMessage = parsedErrorMsg; } _invalidCredentials = true; OnError?.Invoke(this, _authPacket.ErrorCode, errorMessage); _openReply?.TriggerError(new AsyncException(ErrorType.Management, _authPacket.ErrorCode, "Authentication error.")); Task.Delay(100).ContinueWith(x => Close()); } else if (_authPacket.Method == EpAuthPacketMethod.Established) { if (_session.Authenticated) { AuthenticatonCompleted(); } else { _invalidCredentials = true; OnError?.Invoke(this, _authPacket.ErrorCode, "Authentication error."); _openReply?.TriggerError(new AsyncException(ErrorType.Management, _authPacket.ErrorCode, "Authentication error.")); Task.Delay(100).ContinueWith(x => Close()); } } else if (_authPacket.Method == EpAuthPacketMethod.IndicationEstablished) { // @TODO: handle multi-factor authentication indication } } } } return offset; } void AuthenticatonCompleted() { if (this.Instance == null) { Server.Instance.Warehouse.Put( Server.Instance.Link + "/" + this.GetHashCode().ToString().Replace("/", "_"), this) .Then(x => { _authenticated = true; Status = EpConnectionStatus.Connected; _openReply?.Trigger(true); _openReply = null; OnReady?.Invoke(this); _session.AuthenticationHandler?.Provider?.Login(_session); //Server?.Membership?.Login(_session); LoginDate = DateTime.Now; }).Error(x => { _openReply?.TriggerError(x); _openReply = null; }); } else { _authenticated = true; Status = EpConnectionStatus.Connected; _session.AuthenticationHandler?.Provider?.Login(_session); OnReady?.Invoke(this); var proxyTypes = Instance.Warehouse.GetProxyTypesByDomain(_remoteDomain); if (proxyTypes != null) { var typeDefNames = new List(); foreach (var kk in proxyTypes) { foreach (var kv in kk.Value) { typeDefNames.Add(kv.Key); } } if (typeDefNames.Count > 0) { GetTypeDefIds(typeDefNames.ToArray()).Then(ids => { var bag = new AsyncBag(); foreach (var id in ids) bag.Add(FetchTypeDef(id, null)); bag.Seal(); bag.Then((o) => { _openReply?.Trigger(true); _openReply = null; }); }).Error(ex => { _openReply.TriggerError(ex); // do nothing, proxies won't work but connection is established }); } else { _openReply?.Trigger(true); _openReply = null; } } else { _openReply?.Trigger(true); _openReply = null; } } } //private void ProcessClientAuth(byte[] data) //{ // if (authPacket.Command == EpAuthPacketCommand.Acknowledge) // { // // if there is a mismatch in authentication // if (session.LocalMethod != authPacket.RemoteMethod // || session.RemoteMethod != authPacket.LocalMethod) // { // openReply?.TriggerError(new Exception("Peer refused authentication method.")); // openReply = null; // } // // Parse remote headers // var dataType = authPacket.DataType.Value; // var (_, parsed) = Codec.ParseSync(dataType, Instance.Warehouse); // var rt = (Map)parsed; // session.RemoteHeaders = rt.Select(x => new KeyValuePair((EpAuthPacketHeader)x.Key, x.Value)); // if (session.LocalMethod == AuthenticationMethod.None) // { // // send establish // SendParams() // .AddUInt8((byte)EpAuthPacketAction.EstablishNewSession) // .Done(); // } // else if (session.LocalMethod == AuthenticationMethod.Credentials // || session.LocalMethod == AuthenticationMethod.Token) // { // var remoteNonce = (byte[])session.RemoteHeaders[EpAuthPacketHeader.Nonce]; // var localNonce = (byte[])session.LocalHeaders[EpAuthPacketHeader.Nonce]; // // send our hash // var hashFunc = SHA256.Create(); // // local nonce + password or token + remote nonce // var challenge = hashFunc.ComputeHash(new BinaryList() // .AddUInt8Array(localNonce) // .AddUInt8Array(localPasswordOrToken) // .AddUInt8Array(remoteNonce) // .ToArray()); // SendParams() // .AddUInt8((byte)EpAuthPacketAction.AuthenticateHash) // .AddUInt8((byte)EpAuthPacketHashAlgorithm.SHA256) // .AddUInt16((ushort)challenge.Length) // .AddUInt8Array(challenge) // .Done(); // } // } // else if (authPacket.Command == EpAuthPacketCommand.Action) // { // if (authPacket.Action == EpAuthPacketAction.AuthenticateHash) // { // var remoteNonce = (byte[])session.RemoteHeaders[EpAuthPacketHeader.Nonce]; // var localNonce = (byte[])session.LocalHeaders[EpAuthPacketHeader.Nonce]; // // check if the server knows my password // var hashFunc = SHA256.Create(); // var challenge = hashFunc.ComputeHash(new BinaryList() // .AddUInt8Array(remoteNonce) // .AddUInt8Array(localPasswordOrToken) // .AddUInt8Array(localNonce) // .ToArray()); // if (challenge.SequenceEqual(authPacket.Challenge)) // { // // send establish request // SendParams() // .AddUInt8((byte)EpAuthPacketAction.EstablishNewSession) // .Done(); // } // else // { // SendParams() // .AddUInt8((byte)EpAuthPacketEvent.ErrorTerminate) // .AddUInt8((byte)ExceptionCode.ChallengeFailed) // .AddUInt16(16) // .AddString("Challenge Failed") // .Done(); // } // } // } // else if (authPacket.Command == EpAuthPacketCommand.Event) // { // if (authPacket.Event == EpAuthPacketEvent.ErrorTerminate // || authPacket.Event == EpAuthPacketEvent.ErrorMustEncrypt // || authPacket.Event == EpAuthPacketEvent.ErrorRetry) // { // invalidCredentials = true; // openReply?.TriggerError(new AsyncException(ErrorType.Management, authPacket.ErrorCode, authPacket.Message)); // openReply = null; // OnError?.Invoke(this, authPacket.ErrorCode, authPacket.Message); // Close(); // } // else if (authPacket.Event == EpAuthPacketEvent.IndicationEstablished) // { // session.Id = authPacket.SessionId; // session.AuthorizedAccount = authPacket.AccountId.GetString(0, (uint)authPacket.AccountId.Length); // ready = true; // Status = EpConnectionStatus.Connected; // // put it in the warehouse // if (this.Instance == null) // { // Server.Instance.Warehouse.Put(Server + "/" + session.AuthorizedAccount.Replace("/", "_"), this) // .Then(x => // { // openReply?.Trigger(true); // OnReady?.Invoke(this); // openReply = null; // }).Error(x => // { // openReply?.TriggerError(x); // openReply = null; // }); // } // else // { // openReply?.Trigger(true); // openReply = null; // OnReady?.Invoke(this); // } // // start perodic keep alive timer // keepAliveTimer.Start(); // } // else if (authPacket.Event == EpAuthPacketEvent.IAuthPlain) // { // var dataType = authPacket.DataType.Value; // var (_, parsed) = Codec.ParseSync(dataType, Instance.Warehouse); // var rt = (Map)parsed; // var headers = rt.Select(x => new KeyValuePair((EpAuthPacketIAuthHeader)x.Key, x.Value)); // var iAuthRequest = new AuthorizationRequest(headers); // if (Authenticator == null) // { // SendParams() // .AddUInt8((byte)EpAuthPacketEvent.ErrorTerminate) // .AddUInt8((byte)ExceptionCode.NotSupported) // .AddUInt16(13) // .AddString("Not supported") // .Done(); // } // else // { // Authenticator(iAuthRequest).Then(response => // { // SendParams() // .AddUInt8((byte)EpAuthPacketAction.IAuthPlain) // .AddUInt32((uint)headers[EpAuthPacketIAuthHeader.Reference]) // .AddUInt8Array(Codec.Compose(response, this.Instance.Warehouse, this)) // .Done(); // }) // .Timeout(iAuthRequest.Timeout * 1000, // () => // { // SendParams() // .AddUInt8((byte)EpAuthPacketEvent.ErrorTerminate) // .AddUInt8((byte)ExceptionCode.Timeout) // .AddUInt16(7) // .AddString("Timeout") // .Done(); // }); // } // } // else if (authPacket.Event == EpAuthPacketEvent.IAuthHashed) // { // var dataType = authPacket.DataType.Value; // var (_, parsed) = Codec.ParseSync(dataType, Instance.Warehouse); // var rt = (Map)parsed; // var headers = rt.Select(x => new KeyValuePair((EpAuthPacketIAuthHeader)x.Key, x.Value)); // var iAuthRequest = new AuthorizationRequest(headers); // if (Authenticator == null) // { // SendParams() // .AddUInt8((byte)EpAuthPacketEvent.ErrorTerminate) // .AddUInt8((byte)ExceptionCode.NotSupported) // .AddUInt16(13) // .AddString("Not supported") // .Done(); // } // else // { // Authenticator(iAuthRequest).Then(response => // { // var sha = SHA256.Create(); // var hash = sha.ComputeHash(new BinaryList() // .AddUInt8Array((byte[])session.LocalHeaders[EpAuthPacketHeader.Nonce]) // .AddUInt8Array(Codec.Compose(response, this.Instance.Warehouse, this)) // .AddUInt8Array((byte[])session.RemoteHeaders[EpAuthPacketHeader.Nonce]) // .ToArray()); // SendParams() // .AddUInt8((byte)EpAuthPacketAction.IAuthHashed) // .AddUInt32((uint)headers[EpAuthPacketIAuthHeader.Reference]) // .AddUInt8((byte)EpAuthPacketHashAlgorithm.SHA256) // .AddUInt16((ushort)hash.Length) // .AddUInt8Array(hash) // .Done(); // }) // .Timeout(iAuthRequest.Timeout * 1000, // () => // { // SendParams() // .AddUInt8((byte)EpAuthPacketEvent.ErrorTerminate) // .AddUInt8((byte)ExceptionCode.Timeout) // .AddUInt16(7) // .AddString("Timeout") // .Done(); // }); // } // } // else if (authPacket.Event == EpAuthPacketEvent.IAuthEncrypted) // { // throw new NotImplementedException("IAuthEncrypted not implemented."); // } // } //} //private void ProcessHostAuth(byte[] data) //{ // if (authPacket.Command == EpAuthPacketCommand.Initialize) // { // // Parse headers // var dataType = authPacket.DataType.Value; // var (_, parsed) = Codec.ParseSync(dataType, Server.Instance.Warehouse); // var rt = (Map)parsed; // session.RemoteHeaders = rt.Select(x => new KeyValuePair((EpAuthPacketHeader)x.Key, x.Value)); // session.RemoteMethod = authPacket.LocalMethod; // if (authPacket.Initialization == EpAuthPacketInitialize.CredentialsNoAuth) // { // try // { // var username = (string)session.RemoteHeaders[EpAuthPacketHeader.Username]; // var domain = (string)session.RemoteHeaders[EpAuthPacketHeader.Domain]; // //var remoteNonce = (byte[])session.RemoteHeaders[EpAuthPacketHeader.Nonce]; // if (Server.Membership == null) // { // var errMsg = DC.ToBytes("Membership not set."); // SendParams() // .AddUInt8((byte)EpAuthPacketEvent.ErrorTerminate) // .AddUInt8((byte)ExceptionCode.GeneralFailure) // .AddUInt16((ushort)errMsg.Length) // .AddUInt8Array(errMsg) // .Done(); // } // else Server.Membership.UserExists(username, domain).Then(x => // { // if (x != null) // { // session.AuthorizedAccount = x; // var localHeaders = session.LocalHeaders.Select(x => new KeyValuePair((byte)x.Key, x.Value)); // SendParams() // .AddUInt8((byte)EpAuthPacketAcknowledge.NoAuthCredentials) // .AddUInt8Array(Codec.Compose(localHeaders, Server.Instance.Warehouse, this)) // .Done(); // } // else // { // // Send user not found error // SendParams() // .AddUInt8((byte)EpAuthPacketEvent.ErrorTerminate) // .AddUInt8((byte)ExceptionCode.UserOrTokenNotFound) // .AddUInt16(14) // .AddString("User not found") // .Done(); // } // }); // } // catch (Exception ex) // { // // Send the server side error // var errMsg = DC.ToBytes(ex.Message); // SendParams() // .AddUInt8((byte)EpAuthPacketEvent.ErrorTerminate) // .AddUInt8((byte)ExceptionCode.GeneralFailure) // .AddUInt16((ushort)errMsg.Length) // .AddUInt8Array(errMsg) // .Done(); // } // } // else if (authPacket.Initialization == EpAuthPacketInitialize.TokenNoAuth) // { // try // { // if (Server.Membership == null) // { // SendParams() // .AddUInt8((byte)EpAuthPacketEvent.ErrorTerminate) // .AddUInt8((byte)ExceptionCode.UserOrTokenNotFound) // .AddUInt16(15) // .AddString("Token not found") // .Done(); // } // // Check if user and token exists // else // { // var tokenIndex = (ulong)session.RemoteHeaders[EpAuthPacketHeader.TokenIndex]; // var domain = (string)session.RemoteHeaders[EpAuthPacketHeader.Domain]; // //var nonce = (byte[])session.RemoteHeaders[EpAuthPacketHeader.Nonce]; // Server.Membership.TokenExists(tokenIndex, domain).Then(x => // { // if (x != null) // { // session.AuthorizedAccount = x; // var localHeaders = session.LocalHeaders.Select(x => new KeyValuePair((byte)x.Key, x.Value)); // SendParams() // .AddUInt8((byte)EpAuthPacketAcknowledge.NoAuthToken) // .AddUInt8Array(Codec.Compose(localHeaders, this.Instance.Warehouse, this)) // .Done(); // } // else // { // // Send token not found error. // SendParams() // .AddUInt8((byte)EpAuthPacketEvent.ErrorTerminate) // .AddUInt8((byte)ExceptionCode.UserOrTokenNotFound) // .AddUInt16(15) // .AddString("Token not found") // .Done(); // } // }); // } // } // catch (Exception ex) // { // // Sender server side error. // var errMsg = DC.ToBytes(ex.Message); // SendParams() // .AddUInt8((byte)EpAuthPacketEvent.ErrorTerminate) // .AddUInt8((byte)ExceptionCode.GeneralFailure) // .AddUInt16((ushort)errMsg.Length) // .AddUInt8Array(errMsg) // .Done(); // } // } // else if (authPacket.Initialization == EpAuthPacketInitialize.NoAuthNoAuth) // { // try // { // // Check if guests are allowed // if (Server.Membership?.GuestsAllowed ?? true) // { // var localHeaders = session.LocalHeaders.Select(x => new KeyValuePair((byte)x.Key, x.Value)); // session.AuthorizedAccount = "g-" + Global.GenerateCode(); // readyToEstablish = true; // SendParams() // .AddUInt8((byte)EpAuthPacketAcknowledge.NoAuthNoAuth) // .AddUInt8Array(Codec.Compose(localHeaders,Server.Instance.Warehouse, this)) // .Done(); // } // else // { // // Send access denied error because the server does not allow guests. // SendParams() // .AddUInt8((byte)EpAuthPacketEvent.ErrorTerminate) // .AddUInt8((byte)ExceptionCode.AccessDenied) // .AddUInt16(18) // .AddString("Guests not allowed") // .Done(); // } // } // catch (Exception ex) // { // // Send the server side error. // var errMsg = DC.ToBytes(ex.Message); // SendParams() // .AddUInt8((byte)EpAuthPacketEvent.ErrorTerminate) // .AddUInt8((byte)ExceptionCode.GeneralFailure) // .AddUInt16((ushort)errMsg.Length) // .AddUInt8Array(errMsg) // .Done(); // } // } // } // else if (authPacket.Command == EpAuthPacketCommand.Action) // { // if (authPacket.Action == EpAuthPacketAction.AuthenticateHash) // { // var remoteHash = authPacket.Challenge; // AsyncReply reply = null; // try // { // if (session.RemoteMethod == AuthenticationMethod.Credentials) // { // reply = Server.Membership.GetPassword((string)session.RemoteHeaders[EpAuthPacketHeader.Username], // (string)session.RemoteHeaders[EpAuthPacketHeader.Domain]); // } // else if (session.RemoteMethod == AuthenticationMethod.Token) // { // reply = Server.Membership.GetToken((ulong)session.RemoteHeaders[EpAuthPacketHeader.TokenIndex], // (string)session.RemoteHeaders[EpAuthPacketHeader.Domain]); // } // else // { // throw new NotImplementedException("Authentication method unsupported."); // } // reply.Then((pw) => // { // if (pw != null) // { // var localNonce = (byte[])session.LocalHeaders[EpAuthPacketHeader.Nonce]; // var remoteNonce = (byte[])session.RemoteHeaders[EpAuthPacketHeader.Nonce]; // var hashFunc = SHA256.Create(); // var hash = hashFunc.ComputeHash((new BinaryList()) // .AddUInt8Array(remoteNonce) // .AddUInt8Array(pw) // .AddUInt8Array(localNonce) // .ToArray()); // if (hash.SequenceEqual(remoteHash)) // { // // send our hash // var localHash = hashFunc.ComputeHash((new BinaryList()) // .AddUInt8Array(localNonce) // .AddUInt8Array(pw) // .AddUInt8Array(remoteNonce) // .ToArray()); // SendParams() // .AddUInt8((byte)EpAuthPacketAction.AuthenticateHash) // .AddUInt8((byte)EpAuthPacketHashAlgorithm.SHA256) // .AddUInt16((ushort)localHash.Length) // .AddUInt8Array(localHash) // .Done(); // readyToEstablish = true; // } // else // { // //Global.Log("auth", LogType.Warning, "U:" + RemoteUsername + " IP:" + Socket.RemoteEndPoint.Address.ToString() + " S:DENIED"); // SendParams() // .AddUInt8((byte)EpAuthPacketEvent.ErrorTerminate) // .AddUInt8((byte)ExceptionCode.AccessDenied) // .AddUInt16(13) // .AddString("Access Denied") // .Done(); // } // } // }); // } // catch (Exception ex) // { // var errMsg = DC.ToBytes(ex.Message); // SendParams() // .AddUInt8((byte)EpAuthPacketEvent.ErrorTerminate) // .AddUInt8((byte)ExceptionCode.GeneralFailure) // .AddUInt16((ushort)errMsg.Length) // .AddUInt8Array(errMsg) // .Done(); // } // } // else if (authPacket.Action == EpAuthPacketAction.IAuthPlain) // { // var reference = authPacket.Reference; // var dataType = authPacket.DataType.Value; // var (_, value) = Codec.ParseSync(dataType, Instance.Warehouse); // Server.Membership.AuthorizePlain(session, reference, value) // .Then(x => ProcessAuthorization(x)); // } // else if (authPacket.Action == EpAuthPacketAction.IAuthHashed) // { // var reference = authPacket.Reference; // var value = authPacket.Challenge; // var algorithm = authPacket.HashAlgorithm; // Server.Membership.AuthorizeHashed(session, reference, algorithm, value) // .Then(x => ProcessAuthorization(x)); // } // else if (authPacket.Action == EpAuthPacketAction.IAuthEncrypted) // { // var reference = authPacket.Reference; // var value = authPacket.Challenge; // var algorithm = authPacket.PublicKeyAlgorithm; // Server.Membership.AuthorizeEncrypted(session, reference, algorithm, value) // .Then(x => ProcessAuthorization(x)); // } // else if (authPacket.Action == EpAuthPacketAction.EstablishNewSession) // { // if (readyToEstablish) // { // if (Server.Membership == null) // { // ProcessAuthorization(null); // } // else // { // Server.Membership?.Authorize(session).Then(x => // { // ProcessAuthorization(x); // }); // } // //Global.Log("auth", LogType.Warning, "U:" + RemoteUsername + " IP:" + Socket.RemoteEndPoint.Address.ToString() + " S:AUTH"); // } // else // { // SendParams() // .AddUInt8((byte)EpAuthPacketEvent.ErrorTerminate) // .AddUInt8((byte)ExceptionCode.GeneralFailure) // .AddUInt16(9) // .AddString("Not ready") // .Done(); // } // } // } //} //internal void ProcessAuthorization(AuthorizationResults results) //{ // if (results == null || results.Response == Security.Membership.AuthorizationResultsResponse.Success) // { // var r = new Random(); // session.Id = new byte[32]; // r.NextBytes(session.Id); // var accountId = session.AuthorizedAccount.ToBytes(); // SendParams() // .AddUInt8((byte)EpAuthPacketEvent.IndicationEstablished) // .AddUInt8((byte)session.Id.Length) // .AddUInt8Array(session.Id) // .AddUInt8((byte)accountId.Length) // .AddUInt8Array(accountId) // .Done(); // if (this.Instance == null) // { // Server.Instance.Warehouse.Put( // Server.Instance.Link + "/" + this.GetHashCode().ToString().Replace("/", "_"), this) // .Then(x => // { // ready = true; // Status = EpConnectionStatus.Connected; // openReply?.Trigger(true); // openReply = null; // OnReady?.Invoke(this); // Server?.Membership?.Login(session); // LoginDate = DateTime.Now; // }).Error(x => // { // openReply?.TriggerError(x); // openReply = null; // }); // } // else // { // ready = true; // Status = EpConnectionStatus.Connected; // openReply?.Trigger(true); // openReply = null; // OnReady?.Invoke(this); // Server?.Membership?.Login(session); // } // } // else if (results.Response == Security.Membership.AuthorizationResultsResponse.Failed) // { // SendParams() // .AddUInt8((byte)EpAuthPacketEvent.ErrorTerminate) // .AddUInt8((byte)ExceptionCode.ChallengeFailed) // .AddUInt16(21) // .AddString("Authentication failed") // .Done(); // } // else if (results.Response == Security.Membership.AuthorizationResultsResponse.Expired) // { // SendParams() // .AddUInt8((byte)EpAuthPacketEvent.ErrorTerminate) // .AddUInt8((byte)ExceptionCode.Timeout) // .AddUInt16(22) // .AddString("Authentication expired") // .Done(); // } // else if (results.Response == Security.Membership.AuthorizationResultsResponse.ServiceUnavailable) // { // SendParams() // .AddUInt8((byte)EpAuthPacketEvent.ErrorTerminate) // .AddUInt8((byte)ExceptionCode.GeneralFailure) // .AddUInt16(19) // .AddString("Service unavailable") // .Done(); // } // else if (results.Response == Security.Membership.AuthorizationResultsResponse.IAuthPlain) // { // var args = new Map() // { // [EpAuthPacketIAuthHeader.Reference] = results.Reference, // [EpAuthPacketIAuthHeader.Destination] = results.Destination, // [EpAuthPacketIAuthHeader.Trials] = results.Trials, // [EpAuthPacketIAuthHeader.Clue] = results.Clue, // [EpAuthPacketIAuthHeader.RequiredFormat] = results.RequiredFormat, // }.Select(m => new KeyValuePair((byte)m.Key, m.Value)); // SendParams() // .AddUInt8((byte)EpAuthPacketEvent.IAuthPlain) // .AddUInt8Array(Codec.Compose(args, this.Instance.Warehouse, this)) // .Done(); // } // else if (results.Response == Security.Membership.AuthorizationResultsResponse.IAuthHashed) // { // var args = new Map() // { // [EpAuthPacketIAuthHeader.Reference] = results.Reference, // [EpAuthPacketIAuthHeader.Destination] = results.Destination, // [EpAuthPacketIAuthHeader.Expire] = results.Expire, // //[EpAuthPacketIAuthHeader.Issue] = results.Issue, // [EpAuthPacketIAuthHeader.Clue] = results.Clue, // [EpAuthPacketIAuthHeader.RequiredFormat] = results.RequiredFormat, // }.Select(m => new KeyValuePair((byte)m.Key, m.Value)); // SendParams() // .AddUInt8((byte)EpAuthPacketEvent.IAuthHashed) // .AddUInt8Array(Codec.Compose(args, Server.Instance.Warehouse, this)) // .Done(); // } // else if (results.Response == Security.Membership.AuthorizationResultsResponse.IAuthEncrypted) // { // var args = new Map() // { // [EpAuthPacketIAuthHeader.Destination] = results.Destination, // [EpAuthPacketIAuthHeader.Expire] = results.Expire, // [EpAuthPacketIAuthHeader.Clue] = results.Clue, // [EpAuthPacketIAuthHeader.RequiredFormat] = results.RequiredFormat, // }.Select(m => new KeyValuePair((byte)m.Key, m.Value)); // SendParams() // .AddUInt8((byte)EpAuthPacketEvent.IAuthEncrypted) // .AddUInt8Array(Codec.Compose(args, this.Instance.Warehouse, this)) // .Done(); // } //} protected override void DataReceived(NetworkBuffer data) { var msg = data.Read(); uint offset = 0; uint ends = (uint)msg.Length; var packs = new List(); var chunkId = (new Random()).Next(1000, 1000000); this.Socket.Hold(); try { while (offset < ends) { offset = processPacket(msg, offset, ends, data, chunkId); } } catch (Exception ex) { Global.Log(ex); } finally { this.Socket?.Unhold(); } } /// /// Resource interface /// /// Resource trigger. /// public AsyncReply Handle(ResourceOperation trigger, IResourceContext context = null) { if (trigger == ResourceOperation.Configure) { if (context is EpServerConnectionContext serverContext) { _server = serverContext.Server; _serverWarehouse = serverContext.Warehouse; _authPacket = new EpAuthPacket(_serverWarehouse); _packet = new EpPacket(_serverWarehouse); } } else if (trigger == ResourceOperation.Initialize) { if (_authPacket == null) _authPacket = new EpAuthPacket(Instance.Warehouse); if (_packet == null) _packet = new EpPacket(Instance.Warehouse); } else if (trigger == ResourceOperation.Open) { // @TODO: Need a better way to check for initiator or responder if (this.Server != null) return new AsyncReply(true); var host = Instance.Name.Split(':'); var address = host[0]; var port = host.Length > 1 ? ushort.Parse(host[1]) : (ushort)10518; // assign domain from hostname if not provided if (context is EpConnectionContext epContext) { var provider = Instance.Warehouse.TryGetAuthenticationProvider(epContext.AuthenticationProtocol); _remoteDomain = epContext.Domain ?? address; if (provider != null) { _session.AuthenticationHandler = provider.CreateAuthenticationHandler(new AuthenticationContext() { Direction = AuthenticationDirection.Initiator, Domain = _remoteDomain, HostName = address, InitiatorIdentity = epContext.Identity, Mode = epContext.AuthenticationMode, }); } _session.AuthenticationMode = epContext.AuthenticationMode; _session.LocalIdentity = epContext.Identity; ReconnectInterval = epContext.ReconnectInterval; ExceptionLevel = epContext.ExceptionLevel; UseWebSocket = epContext.UseWebSocket; SecureWebSocket = epContext.SecureWebSocket; _remoteDomain = epContext.Domain; AutoReconnect = epContext.AutoReconnect; _hostname = address; _port = port; return Connect(); } else if (_remoteDomain == null) _remoteDomain = address; return Connect(null, address, port, _remoteDomain); } return new AsyncReply(true); } public AsyncReply Connect(ISocket socket = null, string hostname = null, ushort port = 0, string domain = null) { if (_openReply != null) throw new AsyncException(ErrorType.Exception, 0, "Connection in progress"); Status = EpConnectionStatus.Connecting; _openReply = new AsyncReply(); // set auth direction to initiator _authDirection = AuthenticationDirection.Initiator; if (hostname != null) { _session = new Session(); _authDirection = AuthenticationDirection.Initiator; _invalidCredentials = false; _session.LocalHeaders[EpAuthPacketHeader.Domain] = domain; _hostname = hostname; } if (port > 0) this._port = port; if (_session == null) throw new AsyncException(ErrorType.Exception, 0, "Session not initialized"); if (socket == null) { var os = RuntimeInformation.FrameworkDescription; if (UseWebSocket || RuntimeInformation.OSDescription == "Browser") socket = new FrameworkWebSocket(); else socket = new TcpSocket(); } connectSocket(socket); return _openReply; } void connectSocket(ISocket socket) { socket.Connect(this._hostname, this._port).Then(x => { Assign(socket); }).Error((x) => { if (AutoReconnect) { Global.Log("EpConnection", LogType.Debug, "Reconnecting socket..."); Task.Delay((int)ReconnectInterval).ContinueWith((x) => connectSocket(socket)); } else { _openReply.TriggerError(x); _openReply = null; } }); } public async AsyncReply Reconnect() { try { if (!await Connect()) return false; try { var toBeRestored = new List(); foreach (KeyValuePair> kv in _suspendedResources) { EpResource r; if (kv.Value.TryGetTarget(out r)) toBeRestored.Add(r); } foreach (var r in toBeRestored) { var link = DC.ToBytes(r.ResourceLink); Global.Log("EpConnection", LogType.Debug, "Restoreing " + r.ResourceLink); try { var id = (uint)await SendRequest(EpPacketRequest.GetResourceIdByLink, link); // remove from suspended. _suspendedResources.Remove(r.ResourceInstanceId); // id changed ? if (id != r.ResourceInstanceId) r.ResourceInstanceId = id; _neededResources[id] = r; // Reattach using the last-known age so only properties modified while // disconnected are transferred and merged, instead of re-fetching all. await Reattach(id, r.Instance.Age, r); Global.Log("EpConnection", LogType.Debug, "Restored " + id); } catch (AsyncException ex) { if (ex.Code == ExceptionCode.ResourceNotFound) { // skip this resource } else { Global.Log(ex); break; } } } } catch (Exception ex) { Global.Log(ex); } } catch { return false; } OnResumed?.Invoke(this); return true; } /// /// Store interface. /// /// Resource. /// public AsyncReply Put(IResource resource, string path) { if (Codec.IsLocalResource(resource, this)) _neededResources.Add((resource as EpResource).ResourceInstanceId, (EpResource)resource); // else ... send it to the peer return new AsyncReply(true); } public bool Record(IResource resource, string propertyName, object value, ulong? age, DateTime? dateTime) { // nothing to do return true; } public bool Modify(IResource resource, PropertyDef propertyDef, object value, ulong? age, DateTime? dateTime) { // nothing to do return true; } public AsyncBag Children(IResource resource, string name) where T : IResource { throw new Exception("Not implemented"); } protected override void Connected() { if (_authDirection == AuthenticationDirection.Initiator) Declare(); } protected override void Disconnected() { // clean up _authenticated = false; Status = EpConnectionStatus.Closed; _keepAliveTimer.Stop(); // @TODO: lock requests foreach (var x in _requests.Values) { try { x.TriggerError(new AsyncException(ErrorType.Management, 0, "Connection closed")); } catch (Exception ex) { Global.Log(ex); } } foreach (var x in _resourceRequests.Values) { try { x.Reply.TriggerError(new AsyncException(ErrorType.Management, 0, "Connection closed")); } catch (Exception ex) { Global.Log(ex); } } foreach (var x in _typeDefRequests.Values) { try { x.Reply.TriggerError(new AsyncException(ErrorType.Management, 0, "Connection closed")); } catch (Exception ex) { Global.Log(ex); } } //foreach (var x in _typeDefsByNameRequests.Values) //{ // try // { // x.TriggerError(new AsyncException(ErrorType.Management, 0, "Connection closed")); // } // catch (Exception ex) // { // Global.Log(ex); // } //} _requests.Clear(); _resourceRequests.Clear(); _typeDefRequests.Clear(); //_typeDefsByIdRequests.Clear(); //_typeDefsByNameRequests.Clear(); foreach (var x in _attachedResources.Values) { EpResource r; if (x.TryGetTarget(out r)) { r.Suspend(); _suspendedResources[r.ResourceInstanceId] = x; } } if (Server != null) { _suspendedResources.Clear(); UnsubscribeAll(); Instance?.Warehouse?.Remove(this); if (_authenticated) { _session.AuthenticationHandler?.Provider.Logout(_session); //Server.Membership?.Logout(_session); } } else if (AutoReconnect && !_invalidCredentials) { // reconnect Task.Delay((int)ReconnectInterval).ContinueWith((x) => Reconnect()); } else { _suspendedResources.Clear(); } _attachedResources.Clear(); } public AsyncBag Parents(IResource resource, string name) where T : IResource { throw new NotImplementedException(); } public AsyncReply> GetRecord(IResource resource, DateTime fromDate, DateTime toDate) { throw new NotImplementedException(); } AsyncReply IStore.Remove(IResource resource) { // @TODO: this is called when no connection is possible return new AsyncReply(true); } public AsyncReply Remove(string path) { throw new NotImplementedException(); } public AsyncReply Move(IResource resource, string newPath) { throw new NotImplementedException(); } }