/// <summary> /// Causes Master Server to disconnect from the host. /// </summary> public void Disconnect() { if (disconnected) { return; } disconnected = true; Try.Catch_RethrowThreadAbort(keepAlive.Dispose); Try.Catch_RethrowThreadAbort(p.tcpClient.Close); Logger.Info("UpdateComputerLastDisconnectTime: " + ComputerID); Try.Catch_RethrowThreadAbort(() => ServiceWrapper.db.UpdateComputerLastDisconnectTime(ComputerID)); }
/// <summary> /// If this instance has not already stopped, stops the background thread and causes [onStop] to be called. /// </summary> public void Stop() { if (stopped) { return; } stopped = true; Try.Catch_RethrowThreadAbort(keepAliveThread.Abort); try { onStop(this); } catch (ThreadAbortException) { throw; } catch (Exception ex) { Logger.Debug(ex, "Exception thrown when calling onStop action"); } }
/// <summary> /// Gets a list of all users and the list of groups that each user is associated with. /// </summary> /// <returns></returns> public UserAndItsGroups[] GetAllUsersAndTheirGroups() { UserGroup[] allGroups = null; User[] allUsers = null; UserGroupMembership[] allMemberships = null; /// Get all user and group and membership data in a thread-safe manner. db.RunInTransaction(() => { allGroups = db.Table <UserGroup>().ToArray(); allUsers = db.Table <User>().ToArray(); allMemberships = db.Table <UserGroupMembership>().ToArray(); }); // Organize group and user information for efficient access. Dictionary <int, UserGroup> groupMap = new Dictionary <int, UserGroup>(); foreach (UserGroup g in allGroups) { groupMap[g.ID] = g; } Dictionary <int, UserAndItsGroups> userMap = new Dictionary <int, UserAndItsGroups>(); foreach (User u in allUsers) { userMap[u.ID] = new UserAndItsGroups() { User = u } } ; // Associate users and their groups foreach (UserGroupMembership m in allMemberships) { Try.Catch_RethrowThreadAbort(() => { UserAndItsGroups u = userMap[m.UserID]; UserGroup g = groupMap[m.GroupID]; u.Groups.Add(g); }); } return(userMap.Values.ToArray()); }
/// <summary> /// Gets a list of all computers and the list of groups that each computer is associated with. /// </summary> /// <returns></returns> public ComputerAndItsGroups[] GetAllComputersAndTheirGroups() { UserGroup[] allGroups = null; Computer[] allComputers = null; ComputerGroupMembership[] allMemberships = null; /// Get all computer and group and membership data in a thread-safe manner. db.RunInTransaction(() => { allGroups = db.Table <UserGroup>().ToArray(); allComputers = db.Table <Computer>().ToArray(); allMemberships = db.Table <ComputerGroupMembership>().ToArray(); }); // Organize group and computer information for efficient access. Dictionary <int, UserGroup> groupMap = new Dictionary <int, UserGroup>(); foreach (UserGroup g in allGroups) { groupMap[g.ID] = g; } Dictionary <int, ComputerAndItsGroups> computerMap = new Dictionary <int, ComputerAndItsGroups>(); foreach (Computer c in allComputers) { computerMap[c.ID] = new ComputerAndItsGroups() { Computer = c } } ; // Associate computers and their groups foreach (ComputerGroupMembership m in allMemberships) { Try.Catch_RethrowThreadAbort(() => { ComputerAndItsGroups c = computerMap[m.ComputerID]; UserGroup g = groupMap[m.GroupID]; c.Groups.Add(g); }); } return(computerMap.Values.ToArray()); }
private HostConnectResult FollowHostConnectProtocol() { State = HostConnectClientState.Connecting; StateChanged(this, new StateChangedEventArgs(null, State)); Uri masterServerUri = ServiceWrapper.settings.GetMasterServerUri(); // Ensure the certificate exists before we connect, because it can take a moment to create and we don't want the connection to time out. IdentityVerification.EnsureClientCertificateExists(); tcpClient = new TcpClient(); KeepAliveSender keepAlive = null; try { #region Get Connected tcpClient.NoDelay = true; tcpClient.ReceiveTimeout = 30000; tcpClient.SendTimeout = 30000; tcpClient.Connect(masterServerUri.DnsSafeHost, masterServerUri.Port); stream = tcpClient.GetStream(); if (masterServerUri.Scheme == "https") { try { RemoteCertificateValidationCallback certCallback = null; if (!ServiceWrapper.settings.ValidateServerCertificate) { certCallback = (sender, certificate, chain, sslPolicyErrors) => true; } stream = new SslStream(stream, false, certCallback, null); ((SslStream)stream).AuthenticateAsClient(masterServerUri.DnsSafeHost, null, System.Security.Authentication.SslProtocols.Tls12, ServiceWrapper.settings.ValidateServerCertificate); } catch (ThreadAbortException) { throw; } catch (SocketException) { throw; } catch (Exception ex) { Logger.Debug(ex); return(new HostConnectResult(ProtocolErrors.HttpsNegotiationFailed)); } } State = HostConnectClientState.Authenticating; StateChanged(this, new StateChangedEventArgs(null, State)); { // Create HTTP request. StringBuilder sb = new StringBuilder(); sb.Append("POST ").Append(masterServerUri.PathAndQuery).Append("hostconnect HTTP/1.1\r\n"); sb.Append("Host: ").Append(masterServerUri.Host).Append("\r\n"); sb.Append("Content-Length: 0\r\n"); sb.Append("\r\n"); byte[] buf = ByteUtil.Utf8NoBOM.GetBytes(sb.ToString()); stream.Write(buf, 0, buf.Length); } #endregion #region Authentication Protocol // Auth 0) Receive ClientAuthentication command code. Command command = (Command)ByteUtil.ReadNBytes(stream, 1)[0]; if (command != Command.ClientAuthentication) { return(new HostConnectResult(ProtocolErrors.AuthResponseCommand)); } // Auth 1) Receive authentication challenge. This is an array of 32 random bytes which the Host Service must sign with its private key. byte[] authChallenge = ByteUtil.ReadNBytes(stream, 32); // Auth 2) Build authentication reply. // Auth 2.1) Authentication type HostAuthenticationType authType = HostAuthenticationType.PermanentHost; // Auth 2.2) Encode security key byte[] securityKey = ByteUtil.Utf8NoBOM.GetBytes("NOT REAL"); // TODO: Get the real security key from this exe's embedded settings. if (securityKey.Length > byte.MaxValue) { return(new HostConnectResult(ProtocolErrors.SecurityKeyLength)); } byte[] signature, publicKey; if (authType == HostAuthenticationType.PermanentHost) { // Auth 2.3) Create signature signature = IdentityVerification.SignAuthenticationChallenge(authChallenge); // Auth 2.4) Encode public key publicKey = ByteUtil.Utf8NoBOM.GetBytes(IdentityVerification.GetPublicKeyXML()); } else { signature = new byte[0]; publicKey = new byte[0]; } // Auth 2.5) Encode computer name byte[] computerName = ByteUtil.Utf8NoBOM.GetBytes(Environment.MachineName); // Auth 2.6) Encode host version string byte[] appVersion = ByteUtil.Utf8NoBOM.GetBytes(AppVersion.VersionNumber); // Auth 2.7) Encode OS version string byte[] osVersion = ByteUtil.Utf8NoBOM.GetBytes(OSInfo.GetOSVersionInfo()); // Build single buffer (not strictly necessary, but it helps us ensure we got the length calculated right). int calculatedLength = 1 + 1 + securityKey.Length + 2 + signature.Length + 2 + publicKey.Length + 1 + computerName.Length + 1 + appVersion.Length + 1 + osVersion.Length; if (calculatedLength > ushort.MaxValue) { return(new HostConnectResult(ProtocolErrors.AuthResponseTooLarge)); } using (MemoryDataStream mds = new MemoryDataStream(calculatedLength)) { mds.WriteByte((byte)authType); mds.WriteByte((byte)securityKey.Length); mds.Write(securityKey); mds.WriteUInt16((ushort)signature.Length); mds.Write(signature); mds.WriteUInt16((ushort)publicKey.Length); mds.Write(publicKey); mds.WriteByte((byte)computerName.Length); mds.Write(computerName); mds.WriteByte((byte)appVersion.Length); mds.Write(appVersion); mds.WriteByte((byte)osVersion.Length); mds.Write(osVersion); if (mds.Position != calculatedLength) { return(new HostConnectResult(ProtocolErrors.AuthResponseSizeError)); } mds.Seek(0, SeekOrigin.Begin); // Send authentication reply stream.WriteByte((byte)Command.ClientAuthentication); ByteUtil.WriteUInt16((ushort)calculatedLength, stream); mds.CopyTo(stream); } #endregion // This is the Host Service, which is responsible for sending a KeepAlive packet after 60 seconds of sending inactivity. The Master Server will do the same on a 120 second interval. tcpClient.ReceiveTimeout = 135000; // 120 seconds + 15 seconds for bad network conditions. tcpClient.SendTimeout = 75000; State = HostConnectClientState.Connected; StateChanged(this, new StateChangedEventArgs(null, State)); // Send KeepAlive packets every 60 seconds if no other packets have been sent. keepAlive = new KeepAliveSender("KeepAlive", 60000, SendKeepalive, (ignoredArg) => Disconnect()); CommandLoop(tcpClient, stream); } finally { // Make local copies of these references so they can't become null after the null check. KeepAliveSender k = keepAlive; if (k != null) { Try.Catch_RethrowThreadAbort(keepAlive.Stop); } TcpClient c = tcpClient; if (c != null) { Try.Catch_RethrowThreadAbort(c.Close); } } return(new HostConnectResult()); }
private string GetCleanMAC(string MAC) { Try.Catch_RethrowThreadAbort(() => MAC = MAC.Replace(":", "").Replace("-", "").Substring(0, 6)); return(MAC); }