diff --git a/DesktopEdge/App.xaml b/DesktopEdge/App.xaml index 4137cd29..bcd9158d 100644 --- a/DesktopEdge/App.xaml +++ b/DesktopEdge/App.xaml @@ -149,5 +149,23 @@ + + + diff --git a/DesktopEdge/MainWindow.xaml.cs b/DesktopEdge/MainWindow.xaml.cs index 31a20849..c67c738d 100644 --- a/DesktopEdge/MainWindow.xaml.cs +++ b/DesktopEdge/MainWindow.xaml.cs @@ -68,8 +68,8 @@ namespace ZitiDesktopEdge { ExpectedLogPathServices = Path.Combine(ExpectedLogPathRoot, "service", $"ziti-tunneler.log"); } - private void IdentityMenu_OnMessage(string message) { - ShowBlurbAsync(message, ""); + async private void IdentityMenu_OnMessage(string message) { + await ShowBlurbAsync(message, ""); } private void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e) { @@ -105,14 +105,14 @@ namespace ZitiDesktopEdge { /// The MFA Event private void ServiceClient_OnMfaEvent(object sender, MfaEvent mfa) { HideLoad(); - this.Dispatcher.Invoke(() => { + this.Dispatcher.Invoke(async () => { if (mfa.Action == "enrollment_challenge") { string url = HttpUtility.UrlDecode(mfa.ProvisioningUrl); string secret = HttpUtility.ParseQueryString(url)["secret"]; this.IdentityMenu.Identity.MFAInfo.RecoveryCodes = mfa?.RecoveryCodes?.ToArray(); SetupMFA(this.IdentityMenu.Identity, url, secret); } else if (mfa.Action == "auth_challenge") { - ShowBlurbAsync("Setting Up auth_challenge", ""); + await ShowBlurbAsync("Setting Up auth_challenge", ""); } else if (mfa.Action == "enrollment_verification") { if (mfa.Successful) { ShowMFARecoveryCodes(this.IdentityMenu.Identity); @@ -127,7 +127,7 @@ namespace ZitiDesktopEdge { } else if (mfa.Action == "enrollment_remove") { // ShowBlurb("removed mfa: " + mfa.Successful, ""); } else { - ShowBlurbAsync("Unexpected error when processing MFA", ""); + await ShowBlurbAsync ("Unexpected error when processing MFA", ""); logger.Error("unexpected action: " + mfa.Action); } LoadIdentities(true); @@ -172,9 +172,9 @@ namespace ZitiDesktopEdge { /// Show the MFA Recovery Codes /// /// The Ziti Identity to Authenticate - public void ShowMFARecoveryCodes(ZitiIdentity identity) { + async public void ShowMFARecoveryCodes(ZitiIdentity identity) { if (identity.MFAInfo!=null) { - if (identity.MFAInfo.RecoveryCodes?.Length > 0) { + if (identity.MFAInfo.IsAuthenticated&& identity.MFAInfo.RecoveryCodes!=null) { MFASetup.Opacity = 0; MFASetup.Visibility = Visibility.Visible; MFASetup.Margin = new Thickness(0, 0, 0, 0); @@ -188,7 +188,7 @@ namespace ZitiDesktopEdge { this.ShowMFA(IdentityMenu.Identity, 2); } } else { - ShowBlurbAsync("MFA is not setup on this Identity", ""); + await ShowBlurbAsync("MFA is not setup on this Identity", ""); } } @@ -232,7 +232,7 @@ namespace ZitiDesktopEdge { /// /// Close the MFA Screen with animation /// - private void DoClose(bool isComplete) { + async private void DoClose(bool isComplete) { DoubleAnimation animation = new DoubleAnimation(0, TimeSpan.FromSeconds(.3)); ThicknessAnimation animateThick = new ThicknessAnimation(new Thickness(0, 0, 0, 0), TimeSpan.FromSeconds(.3)); animation.Completed += CloseComplete; @@ -252,15 +252,11 @@ namespace ZitiDesktopEdge { if (IdentityMenu.IsVisible) { if (isComplete) { if (MFASetup.Type == 2) { - if (IdentityMenu.Identity.MFAInfo?.RecoveryCodes?.Length > 0) { - ShowRecovery(IdentityMenu.Identity); - } else { - ShowBlurbAsync("You do not have anymore recovery codes", this.RECOVER); - } + ShowRecovery(IdentityMenu.Identity); } else if (MFASetup.Type == 3) { IdentityMenu.Identity.IsMFAEnabled = false; IdentityMenu.Identity.MFAInfo.IsAuthenticated = false; - ShowBlurbAsync("MFA Disabled, Service Access Can Be Limited", ""); + await ShowBlurbAsync("MFA Disabled, Service Access Can Be Limited", ""); } else if (MFASetup.Type == 4) { ShowRecovery(IdentityMenu.Identity); } @@ -346,9 +342,15 @@ namespace ZitiDesktopEdge { this.IdentityMenu.MainWindow = this; SetNotifyIcon("white"); + MFASetup.OnLoad += MFASetup_OnLoad; IdentityMenu.OnMessage += IdentityMenu_OnMessage; } + private void MFASetup_OnLoad(bool isComplete, string title, string message) { + if (isComplete) HideLoad(); + else ShowLoad(title, message); + } + private void Current_SessionEnding(object sender, SessionEndingCancelEventArgs e) { if (notifyIcon != null) { notifyIcon.Visible = false; @@ -487,7 +489,8 @@ namespace ZitiDesktopEdge { serviceClient.OnServiceEvent += ServiceClient_OnServiceEvent; serviceClient.OnTunnelStatusEvent += ServiceClient_OnTunnelStatusEvent; serviceClient.OnMfaEvent += ServiceClient_OnMfaEvent; - serviceClient.OnLogLevelEvent += ServiceClient_OnLogLevelEvent; + serviceClient.OnLogLevelEvent += ServiceClient_OnLogLevelEvent; + serviceClient.OnBulkServiceEvent += ServiceClient_OnBulkServiceEvent; Application.Current.Properties.Add("ServiceClient", serviceClient); monitorClient = new MonitorClient(); @@ -521,6 +524,28 @@ namespace ZitiDesktopEdge { Placement(); } + private void ServiceClient_OnBulkServiceEvent(object sender, BulkServiceEvent e) { + var found = identities.Find(id => id.Fingerprint == e.Fingerprint); + if (found == null) { + logger.Warn($"{e.Action} service event for {e.Fingerprint} but the provided identity fingerprint was not found!"); + return; + } else { + foreach (var removed in e.RemovedServices) { + removeService(found, removed); + } + foreach (var added in e.AddedServices) { + addService(found, added); + } + LoadIdentities(false); + this.Dispatcher.Invoke(() => { + IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu; + if (deets.IsVisible) { + deets.UpdateView(); + } + }); + } + } + string nextVersionStr = null; private void MonitorClient_OnReconnectFailure(object sender, object e) { if (nextVersionStr == null) { @@ -699,6 +724,9 @@ namespace ZitiDesktopEdge { private void ServiceClient_OnClientDisconnected(object sender, object e) { this.Dispatcher.Invoke(() => { + IdentityMenu.Visibility = Visibility.Collapsed; + MFASetup.Visibility = Visibility.Collapsed; + HideModal(); IdList.Children.Clear(); if (e != null) { logger.Debug(e.ToString()); @@ -770,34 +798,20 @@ namespace ZitiDesktopEdge { private void ServiceClient_OnServiceEvent(object sender, ServiceEvent e) { if (e == null) return; - Debug.WriteLine($"==== ServiceEvent : action:{e.Action} fingerprint:{e.Fingerprint} name:{e.Service.Name} "); + logger.Debug($"==== ServiceEvent : action:{e.Action} fingerprint:{e.Fingerprint} name:{e.Service.Name} "); + var found = identities.Find(id => id.Fingerprint == e.Fingerprint); + if (found == null) { + logger.Debug($"{e.Action} service event for {e.Service.Name} but the provided identity fingerprint {e.Fingerprint} is not found!"); + return; + } + + if (e.Action == "added") { + addService(found, e.Service); + } else { + removeService(found, e.Service); + } + LoadIdentities(false); this.Dispatcher.Invoke(() => { - var found = identities.Find(id => id.Fingerprint == e.Fingerprint); - - if (found == null) { - Debug.WriteLine($"{e.Action} service event for {e.Service.Name} but the provided identity fingerprint {e.Fingerprint} is not found!"); - return; - } - - if (e.Action == "added") { - ZitiService zs = new ZitiService(e.Service); - var svc = found.Services.Find(s => s.Name == zs.Name); - if (svc == null) { - found.Services.Add(zs); - if (zs.HasFailingPostureCheck()) { - found.HasServiceFailingPostureCheck = true; - if (zs.PostureChecks.Any(p => !p.IsPassing && p.QueryType == "MFA")) { - found.MFAInfo.IsAuthenticated = false; - } - } - } else { - logger.Debug("the service named " + zs.Name + " is already accounted for on this identity."); - } - } else { - logger.Debug("removing the service named: " + e.Service.Name); - found.Services.RemoveAll(s => s.Name == e.Service.Name); - } - LoadIdentities(false); IdentityDetails deets = ((MainWindow)Application.Current.MainWindow).IdentityMenu; if (deets.IsVisible) { deets.UpdateView(); @@ -805,6 +819,27 @@ namespace ZitiDesktopEdge { }); } + private void addService(ZitiIdentity found, Service added) { + ZitiService zs = new ZitiService(added); + var svc = found.Services.Find(s => s.Name == zs.Name); + if (svc == null) { + found.Services.Add(zs); + if (zs.HasFailingPostureCheck()) { + found.HasServiceFailingPostureCheck = true; + if (zs.PostureChecks.Any(p => !p.IsPassing && p.QueryType == "MFA")) { + found.MFAInfo.IsAuthenticated = false; + } + } + } else { + logger.Debug("the service named " + zs.Name + " is already accounted for on this identity."); + } + } + + private void removeService(ZitiIdentity found, Service removed) { + logger.Debug("removing the service named: {0}", removed.Name); + found.Services.RemoveAll(s => s.Name == removed.Name); + } + private void ServiceClient_OnTunnelStatusEvent(object sender, TunnelStatusEvent e) { if (e == null) return; //just skip it for now... logger.Debug($"==== TunnelStatusEvent: "); @@ -889,6 +924,9 @@ namespace ZitiDesktopEdge { if (!Application.Current.Properties.Contains("dns")) { Application.Current.Properties.Add("dns", status?.IpInfo?.DNS); } + if (!Application.Current.Properties.Contains("dnsenabled")) { + Application.Current.Properties.Add("dnsenabled", status?.AddDns); + } foreach (var id in status.Identities) { updateViewWithIdentity(id); diff --git a/DesktopEdge/Models/MessageCount.cs b/DesktopEdge/Models/MessageCount.cs index c4f76a72..a006f4a7 100644 --- a/DesktopEdge/Models/MessageCount.cs +++ b/DesktopEdge/Models/MessageCount.cs @@ -9,7 +9,7 @@ namespace ZitiDesktopEdge.Models { public int Total { get; set; } public string Message { get; set; } - public string ToString() { + public override string ToString() { return Total + " " + Message; } } diff --git a/DesktopEdge/Properties/AssemblyInfo.cs b/DesktopEdge/Properties/AssemblyInfo.cs index 0ad08d87..ec3cefc3 100644 --- a/DesktopEdge/Properties/AssemblyInfo.cs +++ b/DesktopEdge/Properties/AssemblyInfo.cs @@ -50,6 +50,6 @@ using System.Windows; // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("1.9.2.0")] -[assembly: AssemblyVersion("1.9.2.0")] -[assembly: AssemblyFileVersion("1.9.2.0")] +// [assembly: AssemblyVersion("1.9.3.0")] +[assembly: AssemblyVersion("1.9.3.0")] +[assembly: AssemblyFileVersion("1.9.3.0")] diff --git a/DesktopEdge/Views/Controls/Toggler.xaml.cs b/DesktopEdge/Views/Controls/Toggler.xaml.cs index db20d222..dd0dc4f8 100644 --- a/DesktopEdge/Views/Controls/Toggler.xaml.cs +++ b/DesktopEdge/Views/Controls/Toggler.xaml.cs @@ -54,6 +54,13 @@ namespace ZitiDesktopEdge { } } } + + public void Toggle() { + Enabled = !Enabled; + if (OnToggled != null) { + OnToggled(Enabled); + } + } private void OnToggle(object sender, RoutedEventArgs e) { e.Handled = true; diff --git a/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml b/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml index fdef3fd4..a3d74706 100644 --- a/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml +++ b/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml @@ -12,7 +12,7 @@ - + @@ -24,22 +24,31 @@ + - + + diff --git a/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml.cs b/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml.cs index fb7ef1a9..1b75ed8e 100644 --- a/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml.cs +++ b/DesktopEdge/Views/ItemRenderers/IdentityItem.xaml.cs @@ -115,5 +115,17 @@ namespace ZitiDesktopEdge { private void MFAAuthenticate(object sender, MouseButtonEventArgs e) { this.Authenticate?.Invoke(_identity); } + + private void ToggledSwitch(object sender, MouseButtonEventArgs e) { + ToggleSwitch.Toggle(); + } + + private void DoMFAOrOpen(object sender, MouseButtonEventArgs e) { + if (MfaRequired.Visibility==Visibility.Visible) { + MFAAuthenticate(sender, e); + } else { + OpenDetails(sender, e); + } + } } } diff --git a/DesktopEdge/Views/Screens/IdentityDetails.xaml b/DesktopEdge/Views/Screens/IdentityDetails.xaml index 748816b4..408e801a 100644 --- a/DesktopEdge/Views/Screens/IdentityDetails.xaml +++ b/DesktopEdge/Views/Screens/IdentityDetails.xaml @@ -50,7 +50,7 @@ - + @@ -78,10 +78,41 @@ - - + + + + + + + + + + + + + + + - + + + Name + Address + Protocol + Port + + + + + + + Asc + Desc + + + + + diff --git a/DesktopEdge/Views/Screens/IdentityDetails.xaml.cs b/DesktopEdge/Views/Screens/IdentityDetails.xaml.cs index f15ce065..ebaa8140 100644 --- a/DesktopEdge/Views/Screens/IdentityDetails.xaml.cs +++ b/DesktopEdge/Views/Screens/IdentityDetails.xaml.cs @@ -35,6 +35,10 @@ namespace ZitiDesktopEdge { public event OnAuthenticate Authenticate; public delegate void OnRecovery(ZitiIdentity identity); public event OnRecovery Recovery; + public int Page = 1; + public int PerPage = 50; + public string SortBy = "Name"; + public string SortWay = "Asc"; internal MainWindow MainWindow { get; set; } @@ -53,6 +57,11 @@ namespace ZitiDesktopEdge { set { _identity = value; this.IdDetailToggle.Enabled = _identity.IsEnabled; + Page = 1; + SortBy = "Name"; + SortWay = "Asc"; + SortByField.SelectedIndex = 0; + SortWayField.SelectedIndex = 0; UpdateView(); IdentityArea.Opacity = 1.0; IdentityArea.Visibility = Visibility.Visible; @@ -117,17 +126,45 @@ namespace ZitiDesktopEdge { IdentityMFA.RecoveryButton.Visibility = Visibility.Collapsed; } ServiceList.Children.Clear(); + PageSortArea.Visibility = Visibility.Collapsed; if (_identity.Services.Count>0) { - foreach(var zitiSvc in _identity.Services.OrderBy(s => s.Name.ToLower())) { - if (zitiSvc.Name.ToLower().IndexOf(filter.ToLower()) >=0||zitiSvc.ToString().ToLower().IndexOf(filter.ToLower()) >=0) { - Logger.Trace("painting: " + zitiSvc.Name); - ServiceInfo info = new ServiceInfo(); - info.Info = zitiSvc; - info.OnMessage += Info_OnMessage; - info.OnDetails += ShowDetails; - ServiceList.Children.Add(info); + PageSortArea.Visibility = Visibility.Visible; + int index = 0; + int total = 0; + ZitiService[] services = new ZitiService[0]; + if (SortBy == "Name") services = _identity.Services.OrderBy(s => s.Name.ToLower()).ToArray(); + else if (SortBy == "Address") services = _identity.Services.OrderBy(s => s.Addresses.ToString()).ToArray(); + else if (SortBy == "Protocol") services = _identity.Services.OrderBy(s => s.Protocols.ToString()).ToArray(); + else if (SortBy == "Port") services = _identity.Services.OrderBy(s => s.Ports.ToString()).ToArray(); + if (SortWay == "Desc") services = services.Reverse().ToArray(); + int startIndex = (Page - 1) * PerPage; + for (int i= startIndex; i= 0 || zitiSvc.ToString().ToLower().IndexOf(filter.ToLower()) >= 0) { + Logger.Trace("painting: " + zitiSvc.Name); + ServiceInfo info = new ServiceInfo(); + info.Info = zitiSvc; + info.OnMessage += Info_OnMessage; + info.OnDetails += ShowDetails; + ServiceList.Children.Add(info); + index++; + } } } + + if (Page==1) { + Pages.Items.Clear(); + int totalPages = (total / PerPage) + 1; + for (int i = 1; i <= totalPages; i++) { + ComboBoxItem item = new ComboBoxItem(); + if (i == Page) item.IsSelected = true; + item.Content = i.ToString(); + Pages.Items.Add(item); + } + } + double newHeight = MainHeight - 330; ServiceRow.Height = new GridLength((double)newHeight); MainDetailScroll.MaxHeight = newHeight; @@ -326,5 +363,36 @@ namespace ZitiDesktopEdge { private void AuthFromMessage(object sender, MouseButtonEventArgs e) { this.Authenticate.Invoke(this.Identity); } + + private void Pages_SelectionChanged(object sender, SelectionChangedEventArgs e) { + ComboBoxItem selected = (ComboBoxItem)Pages.SelectedValue; + if (selected!=null) { + int value = Int32.Parse(selected.Content.ToString()); + if (value!=Page) { + Page = value; + UpdateView(); + } + } + } + + private void SortByField_SelectionChanged(object sender, SelectionChangedEventArgs e) { + ComboBoxItem selected = (ComboBoxItem)SortByField.SelectedValue; + if (selected != null && selected.Content!=null) { + if (selected.Content.ToString()!=SortBy) { + SortBy = selected.Content.ToString(); + UpdateView(); + } + } + } + + private void SortWayField_SelectionChanged(object sender, SelectionChangedEventArgs e) { + ComboBoxItem selected = (ComboBoxItem)SortWayField.SelectedValue; + if (selected != null && selected.Content != null) { + if (selected.Content.ToString() != SortWay) { + SortWay = selected.Content.ToString(); + UpdateView(); + } + } + } } } diff --git a/DesktopEdge/Views/Screens/MFAScreen.xaml b/DesktopEdge/Views/Screens/MFAScreen.xaml index 26d15bcf..d71f88d2 100644 --- a/DesktopEdge/Views/Screens/MFAScreen.xaml +++ b/DesktopEdge/Views/Screens/MFAScreen.xaml @@ -13,11 +13,19 @@ - + + + + + + + + + @@ -51,7 +59,7 @@ - + - - - - - - - - + - + + diff --git a/DesktopEdge/Views/Screens/MFAScreen.xaml.cs b/DesktopEdge/Views/Screens/MFAScreen.xaml.cs index 9563833d..e626d46d 100644 --- a/DesktopEdge/Views/Screens/MFAScreen.xaml.cs +++ b/DesktopEdge/Views/Screens/MFAScreen.xaml.cs @@ -31,6 +31,8 @@ namespace ZitiDesktopEdge { public partial class MFAScreen : UserControl { private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + public delegate void LoadEvent(bool isComplete, string title, string message); + public event LoadEvent OnLoad; public delegate void CloseAction(bool isComplete); public event CloseAction OnClose; private string _url = ""; @@ -83,10 +85,14 @@ namespace ZitiDesktopEdge { this._identity = identity; MFAImage.Visibility = Visibility.Visible; SecretCode.Visibility = Visibility.Collapsed; + CloseBlack.Visibility = Visibility.Visible; + CloseWhite.Visibility = Visibility.Collapsed; SecretButton.Content = "Show Secret"; IdName.Content = identity.Name; Logger.Debug($"MFA Url: {url}"); - + AuthBrush.Visibility = Visibility.Collapsed; + MainBrush.Visibility = Visibility.Visible; + MFAImage.Source = CreateQRFromUrl(url); SecretCode.Text = secret; @@ -107,24 +113,39 @@ namespace ZitiDesktopEdge { MFARecoveryArea.Visibility = Visibility.Visible; RecoveryList.Children.Clear(); _codes = codes; + AuthBrush.Visibility = Visibility.Collapsed; + MainBrush.Visibility = Visibility.Visible; + CloseBlack.Visibility = Visibility.Visible; + CloseWhite.Visibility = Visibility.Collapsed; MFAArea.Height = 380; - for (int i=0; i0) { + for (int i = 0; i < codes.Length; i++) { + TextBox label = new TextBox(); + label.Text = codes[i]; + label.BorderThickness = new Thickness(0); + label.Background = new SolidColorBrush(); + label.Background.Opacity = 0; + label.HorizontalAlignment = HorizontalAlignment.Center; + label.HorizontalContentAlignment = HorizontalAlignment.Center; + label.VerticalAlignment = VerticalAlignment.Center; + label.VerticalContentAlignment = VerticalAlignment.Center; + RecoveryList.Children.Add(label); + } + RecoveryList.Visibility = Visibility.Visible; + NoRecovery.Visibility = Visibility.Collapsed; + } else { + RecoveryList.Visibility = Visibility.Collapsed; + NoRecovery.Visibility = Visibility.Visible; } } public void ShowMFA(ZitiIdentity identity, int type) { this.Type = type; AuthCode.Text = ""; + AuthBrush.Visibility = Visibility.Visible; + MainBrush.Visibility = Visibility.Collapsed; + CloseBlack.Visibility = Visibility.Collapsed; + CloseWhite.Visibility = Visibility.Visible; this._identity = identity; MFASetupArea.Visibility = Visibility.Collapsed; MFARecoveryArea.Visibility = Visibility.Collapsed; @@ -152,21 +173,6 @@ namespace ZitiDesktopEdge { } } - private void ToggleRecovery(object sender, MouseButtonEventArgs e) { - AuthCode.Text = ""; - if (AuthCode.MaxLength==6) { - GenerateMFACodes(null, null); - AuthCode.MaxLength = 8; - ToggleType.Content = "Use Auth Code"; - AuthSubTitle.Content = "Enter a recovery code"; - AuthCode.Focus(); - } else { - AuthCode.MaxLength = 6; - ToggleType.Content = "Use Recovery Code"; - AuthSubTitle.Content = "Enter your authorization code"; - AuthCode.Focus(); - } - } async private void ReturnMFACodes(object sender, MouseButtonEventArgs e) { DataClient serviceClient = serviceClient = (DataClient)Application.Current.Properties["ServiceClient"]; string code = AuthCode.Text; @@ -214,17 +220,14 @@ namespace ZitiDesktopEdge { } } - async private void DoAuthenticate(object sender, MouseButtonEventArgs e) { + async private void DoAuthenticate() { if (!this._executing) { + this._executing = true; string code = AuthCode.Text; - if (AuthCode.MaxLength == 8) { - if (code.Length != 8) this.ShowError("You must enter a valid recovery code"); - } else { - if (code.Length != 6) this.ShowError("You must enter a valid code"); - } DataClient serviceClient = (DataClient)Application.Current.Properties["ServiceClient"]; + this.OnLoad?.Invoke(false, "Authentication", "One Moment Please..."); if (this.Type == 1) { SvcResponse authResult = await serviceClient.AuthMFA(this._identity.Fingerprint, code); if (authResult?.Code != 0) { @@ -238,6 +241,7 @@ namespace ZitiDesktopEdge { this.OnClose?.Invoke(true); this._executing = false; } + this.OnLoad?.Invoke(true, "", ""); } else if (this.Type == 2) { MfaRecoveryCodesResponse codeResponse = await serviceClient.ReturnMFACodes(this._identity.Fingerprint, code); if (codeResponse?.Code != 0) { @@ -250,6 +254,7 @@ namespace ZitiDesktopEdge { this.OnClose?.Invoke(true); this._executing = false; } + this.OnLoad?.Invoke(true, "", ""); } else if (this.Type == 3) { SvcResponse authResult = await serviceClient.RemoveMFA(this._identity.Fingerprint, code); if (authResult?.Code != 0) { @@ -261,6 +266,7 @@ namespace ZitiDesktopEdge { this.OnClose?.Invoke(true); this._executing = false; } + this.OnLoad?.Invoke(true, "", ""); } else if (this.Type == 4) { MfaRecoveryCodesResponse codeResponse = await serviceClient.GenerateMFACodes(this._identity.Fingerprint, code); if (codeResponse?.Code != 0) { @@ -273,6 +279,7 @@ namespace ZitiDesktopEdge { this.OnClose?.Invoke(true); this._executing = false; } + this.OnLoad?.Invoke(true, "", ""); } } } @@ -301,7 +308,7 @@ namespace ZitiDesktopEdge { private void AuthCode_KeyUp(object sender, KeyEventArgs e) { if (e.Key == Key.Return) { - DoAuthenticate(sender, null); + DoAuthenticate(); } } } diff --git a/DesktopEdge/Views/Screens/MainMenu.xaml.cs b/DesktopEdge/Views/Screens/MainMenu.xaml.cs index 95ce1a79..aeb5726e 100644 --- a/DesktopEdge/Views/Screens/MainMenu.xaml.cs +++ b/DesktopEdge/Views/Screens/MainMenu.xaml.cs @@ -230,7 +230,7 @@ namespace ZitiDesktopEdge { ConfigIp.Value = Application.Current.Properties["ip"]?.ToString(); ConfigSubnet.Value = Application.Current.Properties["subnet"]?.ToString(); ConfigMtu.Value = Application.Current.Properties["mtu"]?.ToString(); - ConfigDns.Value = Application.Current.Properties["dns"]?.ToString(); + ConfigDns.Value = (bool)Application.Current.Properties["dnsenabled"] ? Application.Current.Properties["dns"]?.ToString() : "disabled"; } else if (menuState == "Identities") { MenuTitle.Content = "Identities"; IdListScrollView.Visibility = Visibility.Visible; diff --git a/DesktopEdge/ZitiDesktopEdge.csproj b/DesktopEdge/ZitiDesktopEdge.csproj index fd06e0a6..b406e57b 100644 --- a/DesktopEdge/ZitiDesktopEdge.csproj +++ b/DesktopEdge/ZitiDesktopEdge.csproj @@ -91,6 +91,7 @@ ..\packages\NLog.4.7.8\lib\net45\NLog.dll + ..\packages\QRCoder.1.4.1\lib\net40\QRCoder.dll diff --git a/Installer/ZitiDesktopEdge.aip b/Installer/ZitiDesktopEdge.aip index 31f61afb..fff0961b 100644 --- a/Installer/ZitiDesktopEdge.aip +++ b/Installer/ZitiDesktopEdge.aip @@ -23,10 +23,10 @@ - + - + diff --git a/ZitiDesktopEdge.Client/DataStructures/DataStructures.cs b/ZitiDesktopEdge.Client/DataStructures/DataStructures.cs index 2d9ade78..c64191bc 100644 --- a/ZitiDesktopEdge.Client/DataStructures/DataStructures.cs +++ b/ZitiDesktopEdge.Client/DataStructures/DataStructures.cs @@ -264,7 +264,11 @@ namespace ZitiDesktopEdge.DataStructures { public int Prefix { get; set; } public override string ToString() { - return Hostname; + if (IsHost) { + return Hostname; + } else { + return IP + "/" + Prefix; + } } } @@ -333,6 +337,7 @@ namespace ZitiDesktopEdge.DataStructures { public string LogLevel { get; set; } public ServiceVersion ServiceVersion { get; set; } + public bool AddDns { get; set; } public void Dump(System.IO.TextWriter writer) { @@ -408,12 +413,17 @@ namespace ZitiDesktopEdge.DataStructures { public List Identities { get; set; } } - public class ServiceEvent : ActionEvent - { + public class ServiceEvent : ActionEvent { public string Fingerprint { get; set; } public Service Service { get; set; } } + public class BulkServiceEvent : ActionEvent { + public string Fingerprint { get; set; } + public List AddedServices { get; set; } + public List RemovedServices { get; set; } + } + public class IdentityEvent : ActionEvent { public Identity Id { get; set; } diff --git a/ZitiDesktopEdge.Client/ServiceClient/DataClient.cs b/ZitiDesktopEdge.Client/ServiceClient/DataClient.cs index 8e628be6..af87b68d 100644 --- a/ZitiDesktopEdge.Client/ServiceClient/DataClient.cs +++ b/ZitiDesktopEdge.Client/ServiceClient/DataClient.cs @@ -35,6 +35,7 @@ namespace ZitiDesktopEdge.ServiceClient { public event EventHandler OnServiceEvent; public event EventHandler OnLogLevelEvent; public event EventHandler OnMfaEvent; + public event EventHandler OnBulkServiceEvent; protected override void ShutdownEvent(StatusEvent e) { Logger.Debug("Clean shutdown detected from ziti"); @@ -57,12 +58,15 @@ namespace ZitiDesktopEdge.ServiceClient { protected virtual void ServiceEvent(ServiceEvent e) { OnServiceEvent?.Invoke(this, e); } - + + protected virtual void BulkServiceEvent(BulkServiceEvent e) { + OnBulkServiceEvent?.Invoke(this, e); + } + protected virtual void LogLevelEvent(LogLevelEvent e) { OnLogLevelEvent?.Invoke(this, e); } - protected virtual void MfaEvent(MfaEvent e) { OnMfaEvent?.Invoke(this, e); } @@ -199,21 +203,6 @@ namespace ZitiDesktopEdge.ServiceClient { return; } - async public Task SetLogLevelAsync(LogLevelEnum level) { - try { - await sendAsync(new SetLogLevelFunction(Enum.GetName(level.GetType(), level))); - SvcResponse resp = await readAsync(ipcReader); - return; - } catch (Exception ioe) { - //almost certainly a problem with the pipe - recreate the pipe... - //setupPipe(); - //throw ioe; - Logger.Error(ioe, "Unexpected error"); - CommunicationError(ioe); - } - return; - } - async public Task IdentityOnOffAsync(string fingerprint, bool onOff) { try { await sendAsync(new IdentityToggleFunction(fingerprint, onOff)); @@ -351,6 +340,14 @@ namespace ZitiDesktopEdge.ServiceClient { ServiceEvent(svc); } break; + case "bulkservice": + //dbg comment Logger.Warn("BULKSERVICE EVENT: \n" + respAsString); + BulkServiceEvent bsvc = serializer.Deserialize(jsonReader); + + if (bsvc != null) { + BulkServiceEvent(bsvc); + } + break; case "logLevel": LogLevelEvent ll = serializer.Deserialize(jsonReader); diff --git a/ZitiUpdateService/Properties/AssemblyInfo.cs b/ZitiUpdateService/Properties/AssemblyInfo.cs index 8c145f2d..6c15d9ab 100644 --- a/ZitiUpdateService/Properties/AssemblyInfo.cs +++ b/ZitiUpdateService/Properties/AssemblyInfo.cs @@ -30,6 +30,6 @@ using System.Runtime.InteropServices; // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: -// [assembly: AssemblyVersion("1.9.2.0")] -[assembly: AssemblyVersion("1.9.2.0")] -[assembly: AssemblyFileVersion("1.9.2.0")] +// [assembly: AssemblyVersion("1.9.3.0")] +[assembly: AssemblyVersion("1.9.3.0")] +[assembly: AssemblyFileVersion("1.9.3.0")] diff --git a/release-notes.md b/release-notes.md index d291aec5..67b66a3f 100644 --- a/release-notes.md +++ b/release-notes.md @@ -1,3 +1,36 @@ +# Release 1.9.3 + +## What's New +* Page Service List +* Sort Service on Identity Details Page +* Consistent UX for MFA Screens + +## Other changes: +* Service to UI interaction is now with one bulk update of services instead of one event per service update +* Added a configuration option to control if DNS is applied to the TUN. Some users are having issues with NRPT-only working. For now we'll add a boolean that allows the user to control if DNS should be added to the TUN + +## Bugs fixed: +* none + +## Dependency Updates +* none + +# Release 1.9.3 + +## What's New +* Page Service List +* Sort Service on Identity Details Page +* Consistent UX for MFA Screens + +## Other changes: +* Service to UI interaction is now with one bulk update of services instead of one event per service update + +## Bugs fixed: +* none + +## Dependency Updates +* none + # Release 1.9.2 ## What's New diff --git a/service/attach.bat b/service/attach.bat new file mode 100644 index 00000000..8d7607dc --- /dev/null +++ b/service/attach.bat @@ -0,0 +1,7 @@ +@echo off +powershell "Get-Process | where -Property Name -eq "ziti-tunnel" | select -ExpandProperty id" > zid +set /p zid= < zid +del zid +echo %zid% + +"c:\Program Files\JetBrains\GoLand 2020.1.1\plugins\go\lib\dlv\windows\dlv.exe" attach %zid% --headless --log --only-same-user=false --api-version=2 --accept-multiclient --listen=localhost:50000 diff --git a/service/cziti/sdk.c b/service/cziti/sdk.c index eff88112..251cec2f 100644 --- a/service/cziti/sdk.c +++ b/service/cziti/sdk.c @@ -151,35 +151,6 @@ dns_manager* get_dns_mgr_from_c() { return &dns_mgr_c; } -/* functions allowing go to iterate tunneleed_service_t* */ -protocol_t* stailq_first_protocol(tunneled_service_t* ts) { - if(ts == NULL || ts->intercept == NULL ) { - return NULL; - } - return STAILQ_FIRST(&ts->intercept->protocols); -} -address_t* stailq_first_address(tunneled_service_t* ts) { - if(ts == NULL || ts->intercept == NULL ) { - return NULL; - } - return STAILQ_FIRST(&ts->intercept->addresses); -} -port_range_t* stailq_first_port_range(tunneled_service_t* ts) { - if(ts == NULL || ts->intercept == NULL ) { - return NULL; - } - return STAILQ_FIRST(&ts->intercept->port_ranges); -} -protocol_t* stailq_next_protocol(protocol_t* cur) { - return STAILQ_NEXT(cur, entries); -} -address_t* stailq_next_address(address_t* cur) { - return STAILQ_NEXT(cur, entries); -} -port_range_t* stailq_next_port_range(port_range_t* cur) { - return STAILQ_NEXT(cur, entries); -} - char* ziti_char_array_get(char** arr, int idx) { return arr ? arr[idx] : NULL; } diff --git a/service/cziti/sdk.go b/service/cziti/sdk.go index 28b01c73..62a44bbe 100644 --- a/service/cziti/sdk.go +++ b/service/cziti/sdk.go @@ -41,37 +41,28 @@ void ziti_dump_to_file(void *ctx, char* outputPath); int ziti_dump_to_log(void *ctx, void* stringsBuilder); void* stailq_first_forgo(void* entries); -protocol_t* stailq_first_protocol(tunneled_service_t* ts); -address_t* stailq_first_address(tunneled_service_t* ts); -port_range_t* stailq_first_port_range(tunneled_service_t* ts); - -protocol_t* stailq_next_protocol(protocol_t* cur); -address_t* stailq_next_address(address_t* cur); -port_range_t* stailq_next_port_range(port_range_t* cur); - */ import "C" import ( + "encoding/json" "errors" - "github.com/openziti/desktop-edge-win/service/windns" "github.com/openziti/desktop-edge-win/service/ziti-tunnel/api" "github.com/openziti/desktop-edge-win/service/ziti-tunnel/dto" "github.com/openziti/desktop-edge-win/service/ziti-tunnel/util/logging" + "net" "os" "strings" "sync" "unsafe" ) -const ( - ADDED = "added" - REMOVED = "removed" -) - var log = logging.Logger() var noFileLog = logging.NoFilenameLogger() var Version dto.ServiceVersion -var ServiceChanges = make(chan dto.ServiceEvent, 256) +var BulkServiceChanges = make(chan BulkServiceChange, 32) + +var cCfgZitiTunnelerClientV1 = C.CString("ziti-tunneler-client.v1") +var cCfgInterceptV1 = C.CString("intercept.v1") type sdk struct { libuvCtx *C.libuv_ctx @@ -81,6 +72,13 @@ type ServiceChange struct { Service *ZService ZitiContext *ZIdentity } +type BulkServiceChange struct { + Fingerprint string + HostnamesToAdd map[string]bool + HostnamesToRemove map[string]bool + ServicesToRemove []*dto.Service + ServicesToAdd []*dto.Service +} var _impl sdk @@ -150,54 +148,54 @@ func NewZid(statusChange func(int)) *ZIdentity { return zid } -func (c *ZIdentity) GetMetrics() (int64, int64, bool) { - if c == nil { +func (zid *ZIdentity) GetMetrics() (int64, int64, bool) { + if zid == nil { return 0, 0, false } - if unsafe.Pointer(c.czctx) == C.NULL { - log.Debugf("ziti context is still null. the identity is probably not initialized yet: %s", c.Fingerprint) + if unsafe.Pointer(zid.czctx) == C.NULL { + log.Debugf("ziti context is still null. the identity is probably not initialized yet: %s", zid.Fingerprint) return 0, 0, false } var up, down C.double - C.ziti_get_transfer_rates(c.czctx, &up, &down) + C.ziti_get_transfer_rates(zid.czctx, &up, &down) return int64(up), int64(down), true } -func (c *ZIdentity) UnsafePointer() unsafe.Pointer { - return unsafe.Pointer(c.czctx) +func (zid *ZIdentity) UnsafePointer() unsafe.Pointer { + return unsafe.Pointer(zid.czctx) } -func (c *ZIdentity) AsKey() string { +func (zid *ZIdentity) AsKey() string { return "marker askey" } -func (c *ZIdentity) Status() (int, error) { - return c.status, c.statusErr +func (zid *ZIdentity) Status() (int, error) { + return zid.status, zid.statusErr } -func (c *ZIdentity) setVersionFromId() string { - if len(c.Version) > 0 { - return c.Version +func (zid *ZIdentity) setVersionFromId() string { + if len(zid.Version) > 0 { + return zid.Version } - c.Version = "" - if c != nil { - if c.czctx != nil { - v1 := C.ziti_get_controller_version(c.czctx) + zid.Version = "" + if zid != nil { + if zid.czctx != nil { + v1 := C.ziti_get_controller_version(zid.czctx) return C.GoString(v1.version) } } - return c.Version + return zid.Version } -func (c *ZIdentity) setNameFromId() string { - if len(c.Name) > 0 { - return c.Name +func (zid *ZIdentity) setNameFromId() string { + if len(zid.Name) > 0 { + return zid.Name } - c.Name = "" - if c != nil { - if c.czid != nil { - if c.czid.name != nil { - c.Name = C.GoString(c.czid.name) + zid.Name = "" + if zid != nil { + if zid.czid != nil { + if zid.czid.name != nil { + zid.Name = C.GoString(zid.czid.name) } else { log.Debug("in Name - c.zid.name was nil") } @@ -207,12 +205,12 @@ func (c *ZIdentity) setNameFromId() string { } else { log.Debug("in Name - c was nil") } - return c.Name + return zid.Name } -func (c *ZIdentity) Tags() []string { - if c.czctx != nil && c.czid != nil { - C.c_mapiter(&c.czid.tags) +func (zid *ZIdentity) Tags() []string { + if zid.czctx != nil && zid.czid != nil { + C.c_mapiter(&zid.czid.tags) /* it := C.model_map_iterator(&c.zid.tags) for { @@ -230,11 +228,11 @@ func (c *ZIdentity) Tags() []string { return nil } -func (c *ZIdentity) Controller() string { - if c.czctx != nil { - return C.GoString(C.ziti_get_controller(c.czctx)) +func (zid *ZIdentity) Controller() string { + if zid.czctx != nil { + return C.GoString(C.ziti_get_controller(zid.czctx)) } - return C.GoString(c.Options.controller) + return C.GoString(zid.Options.controller) } func (zid *ZIdentity) Shutdown() { @@ -253,24 +251,84 @@ func doZitiShutdown(async *C.uv_async_t) { C.ziti_shutdown(ctx) } -func serviceCB(ziti_ctx C.ziti_context, service *C.ziti_service, status C.int, zid *ZIdentity) []dto.Address { +type clientV1Cfg struct { + //{"addresses":["eth0.ziti.ranged","eth0.second","eth0.third"],"portRanges":[{"high":80,"low":80},{"high":443,"low":443}],"protocols":["tcp"]} + Addresses []string `json:"addresses"` + PortRanges []dto.PortRange `json:"portRanges"` + Protocols []string `json:"protocols"` +} +type v1ClientCfg struct { + //{"hostname":"192.168.15.15","port":80} + Hostname string `json:"hostname"` + Port int `json:"port"` +} + +func serviceCB(ziti_ctx C.ziti_context, service *C.ziti_service, status C.int, zid *ZIdentity) *dto.Service { if zid == nil { log.Errorf("in serviceCB with nil zid??? ") - return make([]dto.Address, 0) + return nil } name := C.GoString(service.name) svcId := C.GoString(service.id) log.Debugf("============ INSIDE serviceCB - status: %s:%s - %v, %v ============", name, svcId, status, service.perm_flags) - ts := C.ziti_sdk_c_on_service(ziti_ctx, service, status, unsafe.Pointer(theTun.tunCtx)) + C.ziti_sdk_c_on_service(ziti_ctx, service, status, unsafe.Pointer(theTun.tunCtx)) + + var protocols []string + var portRanges []dto.PortRange + var addresses []dto.Address + + strClientV1cfg := C.GoString(C.ziti_service_get_raw_config(service, cCfgInterceptV1)) + if strClientV1cfg != "" { + log.Tracef("intercept.v1: %s", strClientV1cfg) + var obj clientV1Cfg + uerr := json.Unmarshal([]byte(strClientV1cfg), &obj) + if uerr != nil { + log.Errorf("could not marshall json? %v", uerr) + } + protocols = obj.Protocols + portRanges = obj.PortRanges + a := make([]dto.Address, len(obj.Addresses)) + for idx, add := range obj.Addresses { + a[idx] = toAddy(add) + } + + addresses = append(a) + } else { + strZitiTunnelerClientV1 := C.GoString(C.ziti_service_get_raw_config(service, cCfgZitiTunnelerClientV1)) + log.Tracef("ziti-tunneler-client.v1: %s", strZitiTunnelerClientV1) + var obj v1ClientCfg + uerr := json.Unmarshal([]byte(strZitiTunnelerClientV1), &obj) + if uerr != nil { + log.Errorf("could not marshall json? %v", uerr) + } + protocols = []string{"UDP", "TCP"} + portRanges = []dto.PortRange{{Low: obj.Port, High: obj.Port}} + addresses = []dto.Address{toAddy(obj.Hostname)} + } - protocols := getTunneledServiceProtocols(ts) - addresses := getTunneledServiceAddresses(ts) - portRanges := getTunneledServicePortRanges(ts) log.Infof("service update: %s, id: %s, portocols:%s, addresses:%v, portRanges: %v", name, svcId, protocols, addresses, portRanges) + var svc *dto.Service + if status == C.ZITI_SERVICE_UNAVAILABLE { - serviceUnavailable(zid, svcId, name) + log.Debugf("serivce has become unavailable: %s [%s]", name, svcId) + f, ok := zid.Services.Load(svcId) + if ok { + svc = &dto.Service{ + Name: name, + Id: svcId, + Addresses: addresses, + } + found := f.(*ZService) + if found != nil { + found.Service = nil + } + zid.Services.Delete(svcId) + return svc + } else { + log.Warnf("could not remove service? service not found with id: %s, name: %s in context %d", svcId, name, &zid) + } } else if status == C.ZITI_OK { pcIds := make(map[string]bool) var postureChecks []dto.PostureCheck @@ -315,7 +373,7 @@ func serviceCB(ziti_ctx C.ziti_context, service *C.ziti_service, status C.int, z } } - svc := &dto.Service{ + svc = &dto.Service{ Name: name, Id: svcId, Protocols: protocols, @@ -325,46 +383,47 @@ func serviceCB(ziti_ctx C.ziti_context, service *C.ziti_service, status C.int, z PostureChecks: postureChecks, IsAccessable: hasAccess, } - added := ZService{ Name: name, Id: svcId, Service: svc, Czctx: ziti_ctx, } - - se := dto.ServiceEvent{ - ActionEvent: dto.SERVICE_ADDED, - Fingerprint: zid.Fingerprint, - Service: svc, - } - zid.Services.Store(svcId, &added) - go func() {ServiceChanges <- se}() //do this in a go routine just in case it gets blocked so the uv loop doesn't block } - return addresses + + return svc } -func serviceUnavailable(ctx *ZIdentity, svcId string, name string) { - log.Debugf("serivce has become unavailalbe: %s [%s]", name, svcId) - f, ok := ctx.Services.Load(svcId) - if ok { - found := f.(*ZService) - found.Service = nil - ctx.Services.Delete(svcId) - se := dto.ServiceEvent{ - ActionEvent: dto.SERVICE_REMOVED, - Fingerprint: ctx.Fingerprint, - Service: &dto.Service{ - Name: name, - Id: svcId, - }, - } - ServiceChanges <- se - log.Debugf("dispatched serivce has become unavailalbe to ui: %s [%s]", name, svcId) - } else { - log.Warnf("could not remove service? service not found with id: %s, name: %s in context %d", svcId, name, &ctx) +func toAddy(hostOrIpOrCidr string) dto.Address { + addy := dto.Address{ + IsHost: false, + HostName: "", + IP: "", + Prefix: 0, } + + log.Debugf("parsing %s to address", hostOrIpOrCidr) + ip, ipnet, err := net.ParseCIDR(hostOrIpOrCidr) + if err != nil { + //must not be an CIDR... try IP parse + ip = net.ParseIP(hostOrIpOrCidr) + if ip == nil { + log.Debugf("%s does not appear to be an ip/cidr combination. considering it a hostname", hostOrIpOrCidr) + addy.HostName = hostOrIpOrCidr + addy.IsHost = true + } else { + log.Debugf("%s determined to be ip", hostOrIpOrCidr) + addy.IP = ip.String() + } + } else { + log.Debugf("%s appears to be a proper CIDR", hostOrIpOrCidr) + ones, _ := ipnet.Mask.Size() + addy.IP = ip.String() + addy.Prefix = ones + } + log.Tracef("parsed address: %v from %s", addy, hostOrIpOrCidr) + return addy } //export eventCB @@ -402,6 +461,9 @@ func eventCB(ztx C.ziti_context, event *C.ziti_event_t) { case C.ZitiServiceEvent: hostnamesToAdd := make(map[string]bool) hostnamesToRemove := make(map[string]bool) + servicesToRemove := make([]*dto.Service, 0) + servicesToAdd := make([]*dto.Service, 0) + srvEvent := C.ziti_event_service_event(event) for i := 0; true; i++ { @@ -409,11 +471,16 @@ func eventCB(ztx C.ziti_context, event *C.ziti_event_t) { if unsafe.Pointer(removed) == C.NULL { break } - addys := serviceCB(ztx, removed, C.ZITI_SERVICE_UNAVAILABLE, zid) - for _, toRemove := range addys { - if toRemove.IsHost { - hostnamesToRemove[toRemove.HostName] = true + + svcToRemove := serviceCB(ztx, removed, C.ZITI_SERVICE_UNAVAILABLE, zid) + if svcToRemove != nil { + remAddys := svcToRemove.Addresses + for _, toRemove := range remAddys { + if toRemove.IsHost && hostnameRemoved(toRemove.HostName) == 0 { + hostnamesToRemove[toRemove.HostName] = true + } } + servicesToRemove = append(servicesToRemove, svcToRemove) } } for i := 0; true; i++ { @@ -421,19 +488,29 @@ func eventCB(ztx C.ziti_context, event *C.ziti_event_t) { if unsafe.Pointer(changed) == C.NULL { break } + log.Info("service changed remove the service then add it back immediately", C.GoString(changed.name)) - addys := serviceCB(ztx, changed, C.ZITI_SERVICE_UNAVAILABLE, zid) - for _, toRemove := range addys { - if toRemove.IsHost { - hostnamesToRemove[toRemove.HostName] = true + svcToRemove := serviceCB(ztx, changed, C.ZITI_SERVICE_UNAVAILABLE, zid) + if svcToRemove != nil { + + remAddys := svcToRemove.Addresses + for _, toRemove := range remAddys { + if toRemove.IsHost && hostnameRemoved(toRemove.HostName) == 0 { + hostnamesToRemove[toRemove.HostName] = true + } } + servicesToRemove = append(servicesToRemove, svcToRemove) } - addys = serviceCB(ztx, changed, C.ZITI_OK, zid) - for _, toAdd := range addys { - if toAdd.IsHost { - hostnamesToAdd[toAdd.HostName] = true + svcToAdd := serviceCB(ztx, changed, C.ZITI_OK, zid) + if svcToAdd != nil { + addAddys := svcToAdd.Addresses + for _, toAdd := range addAddys { + if toAdd.IsHost && hostnameAdded(toAdd.HostName) == 1 { + hostnamesToAdd[toAdd.HostName] = true + } } + servicesToAdd = append(servicesToAdd, svcToAdd) } } for i := 0; true; i++ { @@ -441,40 +518,37 @@ func eventCB(ztx C.ziti_context, event *C.ziti_event_t) { if unsafe.Pointer(added) == C.NULL { break } - addys := serviceCB(ztx, added, C.ZITI_OK, zid) - for _, toAdd := range addys { - if toAdd.IsHost { - hostnamesToAdd[toAdd.HostName] = true + svcToAdd := serviceCB(ztx, added, C.ZITI_OK, zid) + if svcToAdd != nil { + addAddys := svcToAdd.Addresses + for _, toAdd := range addAddys { + if toAdd.IsHost && hostnameAdded(toAdd.HostName) == 1 { + hostnamesToAdd[toAdd.HostName] = true + } } + servicesToAdd = append(servicesToAdd, svcToAdd) } } - if len(hostnamesToAdd) > 0 { - windns.AddNrptRules(hostnamesToAdd, dnsip.String()) - log.Infof("mapped the following hostnames: %v", hostnamesToAdd) + svcChange := BulkServiceChange{ + Fingerprint: zid.Fingerprint, + HostnamesToAdd: hostnamesToAdd, + HostnamesToRemove: hostnamesToRemove, + ServicesToAdd: servicesToAdd, + ServicesToRemove: servicesToRemove, } - if len(hostnamesToRemove) > 0 { - windns.RemoveNrptRules(hostnamesToRemove) - log.Infof("unmapped the following hostnames: %v", hostnamesToRemove) + if len(BulkServiceChanges) == cap(BulkServiceChanges) { + log.Warn("Service changes are not being processed fast enough. This client is out of date from the controller! This is unexpected. If you see this warning please report") + } else { + BulkServiceChanges <- svcChange } - - var m = dto.IdentityEvent{ - ActionEvent: dto.IdentityUpdateComplete, - Id: dto.Identity{ - FingerPrint: zid.Fingerprint, - }, - } - - goapi.BroadcastEvent(m) - default: log.Infof("event %d not handled", event._type) } } func zitiContextEvent(ztx C.ziti_context, status C.int, zid *ZIdentity) { - zid.status = int(status) zid.statusErr = zitiError(status) zid.czctx = ztx @@ -605,81 +679,35 @@ func ziti_dump_go_to_log_cb(stringsBuilder unsafe.Pointer, charData *C.char) { sb := (*strings.Builder)(stringsBuilder) sb.WriteString(C.GoString(charData)) } -func ZitiDumpOnShutdown(zid *ZIdentity, sb *strings.Builder) { - C.ziti_dump_to_log(unsafe.Pointer(zid.czctx), unsafe.Pointer(sb)) -} - -func getTunneledServiceProtocols(ts *C.tunneled_service_t) []string { - var protocols []string - next := C.stailq_first_protocol(ts) - if unsafe.Pointer(next) != C.NULL { - protocols = append(protocols, C.GoString(next.protocol)) - for { - next = C.stailq_next_protocol(next) - if unsafe.Pointer(next) != C.NULL { - protocols = append(protocols, C.GoString(next.protocol)) - } else { - break - } - } - } - return protocols -} - -func getTunneledServicePortRanges(ts *C.tunneled_service_t) []dto.PortRange { - var values []dto.PortRange - next := C.stailq_first_port_range(ts) - if unsafe.Pointer(next) != C.NULL { - p := dto.PortRange{ - High: int(next.high), - Low: int(next.low), - } - values = append(values, p) - for { - next = C.stailq_next_port_range(next) - if unsafe.Pointer(next) != C.NULL { - p := dto.PortRange{ - High: int(next.high), - Low: int(next.low), - } - values = append(values, p) - } else { - break - } - } - } - return values -} - -func getTunneledServiceAddresses(ts *C.tunneled_service_t) []dto.Address { - var values []dto.Address - next := C.stailq_first_address(ts) - if unsafe.Pointer(next) != C.NULL { - p := dto.Address{ - IsHost: bool(next.is_hostname), - HostName: C.GoString(&next.str[0]), - IP: C.GoString(C.ipaddr_ntoa(&next.ip)), - Prefix: int(next.prefix_len), - } - values = append(values, p) - for { - next = C.stailq_next_address(next) - if unsafe.Pointer(next) != C.NULL { - p := dto.Address{ - IsHost: bool(next.is_hostname), - HostName: C.GoString(&next.str[0]), - IP: C.GoString(C.ipaddr_ntoa(&next.ip)), - Prefix: int(next.prefix_len), - } - values = append(values, p) - } else { - break - } - } - } - return values +func ZitiDumpOnShutdown(zid *ZIdentity) { + sb := strings.Builder{} + C.ziti_dump_to_log(unsafe.Pointer(zid.czctx), unsafe.Pointer(&sb)) + log.Infof("working around the c sdk's limitation of embedding newlines on calling ziti_shutdown\n %s", sb.String()) } func InitTunnelerDns(ipBase uint32, mask int) { C.ziti_tunneler_init_dns(C.uint32_t(ipBase), C.int(mask)) } + +var addressCount = make(map[string]int) + +func hostnameAdded(host string) int { + count := 1 + if cur, ok := addressCount[host]; ok { + count = cur + 1 + } + addressCount[host] = count + log.Debugf("hostname added: %s. count now: %d", host, count) + return count +} +func hostnameRemoved(host string) int { + count := 0 + if cur, ok := addressCount[host]; ok { + if cur > 0 { + count = cur - 1 + } + } + addressCount[host] = count + log.Debugf("hostname removed %s. count now: %d", host, count) + return count +} diff --git a/service/debug.bat b/service/debug.bat index 0775e9bd..bb99fe88 100644 --- a/service/debug.bat +++ b/service/debug.bat @@ -1 +1 @@ -cls & "c:\Program Files\JetBrains\GoLand 2020.1.1\plugins\go\lib\dlv\windows\dlv.exe" --listen=localhost:50000 --headless=true --api-version=2 --check-go-version=false --only-same-user=false exec c:\git\github\openziti\desktop-edge-win\service\ziti-tunnel.exe debug +cls & "c:\Program Files\JetBrains\GoLand 2020.1.1\plugins\go\lib\dlv\windows\dlv.exe" --listen=localhost:50000 --headless=true --api-version=2 --check-go-version=false --only-same-user=false exec ziti-tunnel.exe debug diff --git a/service/windns/windns.go b/service/windns/windns.go index 54dd0cbf..ae2897c0 100644 --- a/service/windns/windns.go +++ b/service/windns/windns.go @@ -27,7 +27,12 @@ import ( "strings" ) +const ( + MAX_POWERSHELL_SCRIPT_LEN = 7500 //represents how long the powershell script can be. as of apr 2021 the limit was 8k (8192). leaves a little room for the rest of the script +) + var log = logging.Logger() +var exeName = "ziti-tunnel" func GetConnectionSpecificDomains() []string { script := `Get-DnsClient | Select-Object ConnectionSpecificSuffix -Unique | ForEach-Object { $_.ConnectionSpecificSuffix }; (Get-DnsClientGlobalSetting).SuffixSearchList` @@ -103,8 +108,8 @@ func FlushDNS() { } func RemoveAllNrptRules() { - script := fmt.Sprintf(`Get-DnsClientNrptRule | Where { $_.Comment.StartsWith("Added by ziti-tunnel") } | Remove-DnsClientNrptRule -ErrorAction SilentlyContinue -Force`) - log.Debugf("removing all nrpt rules with: %s", script) + script := fmt.Sprintf(`Get-DnsClientNrptRule | Where { $_.Comment.StartsWith("Added by %s") } | Remove-DnsClientNrptRule -ErrorAction SilentlyContinue -Force`, exeName) + log.Tracef("removing all nrpt rules with: %s", script) cmd := exec.Command("powershell", "-Command", script) cmd.Stderr = os.Stdout @@ -116,24 +121,27 @@ func RemoveAllNrptRules() { } } +var namespaceTemplate = `%s@{n="%s";}` +var namespaceTemplatePadding = len(namespaceTemplate) + func AddNrptRules(domainsToMap map[string]bool, dnsServer string) { if len(domainsToMap) == 0 { log.Debug("no domains to map specified to AddNrptRules. exiting early") return } - blockSize := 50 - if len(domainsToMap) > blockSize { - log.Debugf("domainsToMap is too long [%d] and may fail on some systems. splitting the requested entries into blocks of %d", len(domainsToMap), blockSize) - } + maxBucketSize := 500 currentSize := 0 - hostnames := make([]string, blockSize) + hostnames := make([]string, maxBucketSize) + ruleSize := 0 for hostname := range domainsToMap { - if currentSize >= blockSize { + ruleSize = ruleSize + len(hostname) + namespaceTemplatePadding + if ruleSize > MAX_POWERSHELL_SCRIPT_LEN || currentSize >= maxBucketSize { log.Debugf("sending chunk of domains to be added to NRPT") - chunkedAddNrptRules(hostnames, dnsServer) - hostnames = make([]string, blockSize) + chunkedAddNrptRules(hostnames[:currentSize], dnsServer) + hostnames = make([]string, maxBucketSize) currentSize = 0 + ruleSize = len(hostname) + namespaceTemplatePadding } hostnames[currentSize] = hostname currentSize++ @@ -146,21 +154,23 @@ func AddNrptRules(domainsToMap map[string]bool, dnsServer string) { func chunkedAddNrptRules(domainsToAdd []string, dnsServer string) { sb := strings.Builder{} - sb.WriteString(`$Rules = @( + sb.WriteString(`$Namespaces = @( `) for _, hostname := range domainsToAdd { - sb.WriteString(fmt.Sprintf(`@{ Namespace ="%s"; NameServers = @("%s"); Comment = "Added by ziti-tunnel"; DisplayName = "ziti-tunnel:%s"; }%s`, hostname, dnsServer, hostname, "\n")) + sb.WriteString(fmt.Sprintf(namespaceTemplate, "\n", hostname)) } sb.WriteString(fmt.Sprintf(`) -ForEach ($Rule in $Rules) { - Add-DnsClientNrptRule @Rule -}`)) +ForEach ($Namespace in $Namespaces) { + $ns=$Namespace["n"] + $Rule = @{Namespace="${ns}"; NameServers=@("%s"); Comment="Added by %s"; DisplayName="%s:${ns}"; } + Add-DnsClientNrptRule @Rule +}`, dnsServer, exeName, exeName)) script := sb.String() - log.Debugf("Executing NRPT script containing %d domains:\n%s", len(domainsToAdd), script) + log.Tracef("Executing ADD NRPT script containing %d domains. total script size: %d\n%s", len(domainsToAdd), len(script), script) cmd := exec.Command("powershell", "-Command", script) cmd.Stderr = os.Stdout @@ -172,27 +182,52 @@ ForEach ($Rule in $Rules) { } } -func RemoveNrptRules(domainsToMap map[string]bool) { - if len(domainsToMap) == 0 { +func RemoveNrptRules(domainsToRemove map[string]bool) { + if len(domainsToRemove) == 0 { log.Debug("no domains to map specified to RemoveNrptRules. exiting early") return } - sb := strings.Builder{} - sb.WriteString(`$toRemove = @( -`) + maxBucketSize := 500 + currentSize := 0 + hostnames := make([]string, maxBucketSize) + ruleSize := 0 + for hostname := range domainsToRemove { + ruleSize = ruleSize + len(hostname) + namespaceTemplatePadding + if ruleSize > MAX_POWERSHELL_SCRIPT_LEN || currentSize >= maxBucketSize { + log.Debugf("sending chunk of domains to be added to NRPT") + chunkedRemoveNrptRules(hostnames[:currentSize]) + hostnames = make([]string, maxBucketSize) + currentSize = 0 + ruleSize = len(hostname) + namespaceTemplatePadding + } + hostnames[currentSize] = hostname + currentSize++ + } - for hostname := range domainsToMap { - sb.WriteString(fmt.Sprintf(`"%s"%s`, hostname, "\n")) + if currentSize > 0 { + //means there's a chunk still to add.... + chunkedRemoveNrptRules(hostnames[:currentSize]) + } +} + +func chunkedRemoveNrptRules(domainsToRemove []string) { + sb := strings.Builder{} + sb.WriteString(`$toRemove = @(`) + + for _, hostname := range domainsToRemove { + sb.WriteString(fmt.Sprintf(namespaceTemplate, "\n", hostname)) } sb.WriteString(fmt.Sprintf(`) -Get-DnsClientNrptRule | Where { $toRemove -contains $_.DisplayName } | Remove-DnsClientNrptRule -ErrorAction SilentlyContinue -Force +ForEach ($ns in $toRemove){ + Get-DnsClientNrptRule | where Namespace -eq $ns["n"] | Remove-DnsClientNrptRule -Force -ErrorAction SilentlyContinue +} `)) script := sb.String() - log.Debugf("Executing NRPT script:\n%s", script) + log.Tracef("Executing REMOVE NRPT script containing %d domains. total script size: %d\n%s", len(domainsToRemove), len(script), script) cmd := exec.Command("powershell", "-Command", script) cmd.Stderr = os.Stdout diff --git a/service/ziti-tunnel/cli/ipcClient.go b/service/ziti-tunnel/cli/ipcClient.go index c2316fe3..50a87b94 100644 --- a/service/ziti-tunnel/cli/ipcClient.go +++ b/service/ziti-tunnel/cli/ipcClient.go @@ -18,9 +18,9 @@ package cli */ import ( - "fmt" "bufio" "encoding/json" + "fmt" "io" "net" "strings" diff --git a/service/ziti-tunnel/dto/dtos.go b/service/ziti-tunnel/dto/dtos.go index d63105f6..c8930d43 100644 --- a/service/ziti-tunnel/dto/dtos.go +++ b/service/ziti-tunnel/dto/dtos.go @@ -119,6 +119,7 @@ type TunnelStatus struct { TunIpv4 string TunIpv4Mask int Status string + AddDns bool } type ServiceVersion struct { @@ -152,10 +153,11 @@ type MetricsEvent struct { Identities []*Identity } -type ServiceEvent struct { +type BulkServiceEvent struct { ActionEvent - Fingerprint string - Service *Service + Fingerprint string + AddedServices []*Service + RemovedServices []*Service } type IdentityEvent struct { diff --git a/service/ziti-tunnel/dto/events.go b/service/ziti-tunnel/dto/events.go index c216ced3..e2e43379 100644 --- a/service/ziti-tunnel/dto/events.go +++ b/service/ziti-tunnel/dto/events.go @@ -18,17 +18,19 @@ package dto const ( ADDED = "added" + BULK = "bulk" REMOVED = "removed" ERROR = "error" UPDATED = "updated" CHANGED = "changed" NORMAL = "Normal" - SERVICE_OP = "service" - IDENTITY_OP = "identity" - LOGLEVEL_OP = "logLevel" - FEEDBACK_OP = "CaptureLogs" - MFA_OP = "mfa" + SERVICE_OP = "service" + BULK_SERVICE_OP = "bulkservice" + IDENTITY_OP = "identity" + LOGLEVEL_OP = "logLevel" + FEEDBACK_OP = "CaptureLogs" + MFA_OP = "mfa" MFAEnrollmentChallengAtion = "enrollment_challenge" MFAEnrollmentVerificationAction = "enrollment_verification" @@ -45,6 +47,10 @@ var SERVICE_REMOVED = ActionEvent{ StatusEvent: StatusEvent{Op: SERVICE_OP}, Action: REMOVED, } +var SERVICE_BULK = ActionEvent{ + StatusEvent: StatusEvent{Op: BULK_SERVICE_OP}, + Action: BULK, +} var IDENTITY_ADDED = ActionEvent{ StatusEvent: StatusEvent{Op: IDENTITY_OP}, diff --git a/service/ziti-tunnel/service/debug.go b/service/ziti-tunnel/service/debug.go index 47f87435..2050ff80 100644 --- a/service/ziti-tunnel/service/debug.go +++ b/service/ziti-tunnel/service/debug.go @@ -25,11 +25,11 @@ import ( func dbg() { r := rts.ToStatus(true) - events.broadcast <- dto.TunnelStatusEvent{ + rts.BroadcastEvent(dto.TunnelStatusEvent{ StatusEvent: dto.StatusEvent{Op: "status"}, Status: r, ApiVersion: API_VERSION, - } + }) svcs := make([]*dto.Service, 2) svcs[0] = &dto.Service{ @@ -39,7 +39,7 @@ func dbg() { Name: "Second Fake Service", } - events.broadcast <- dto.IdentityEvent{ + rts.BroadcastEvent(dto.IdentityEvent{ ActionEvent: dto.IDENTITY_ADDED, Id: dto.Identity{ Name: "NewIdentity", @@ -52,25 +52,9 @@ func dbg() { Services: svcs, Metrics: nil, }, - } + }) - events.broadcast <- dto.ServiceEvent{ - ActionEvent: dto.SERVICE_ADDED, - Service: &dto.Service{ - Name: "New Service", - }, - Fingerprint: "new_id_fingerprint", - } - - events.broadcast <- dto.ServiceEvent{ - ActionEvent: dto.SERVICE_REMOVED, - Service: &dto.Service{ - Name: "New Service", - }, - Fingerprint: "new_id_fingerprint", - } - - events.broadcast <- dto.IdentityEvent{ + rts.BroadcastEvent(dto.IdentityEvent{ ActionEvent: dto.IDENTITY_REMOVED, Id: dto.Identity{ Name: "", @@ -81,5 +65,5 @@ func dbg() { Services: nil, Metrics: nil, }, - } + }) } diff --git a/service/ziti-tunnel/service/ipc.go b/service/ziti-tunnel/service/ipc.go index 04aca3fd..4bd5cf49 100644 --- a/service/ziti-tunnel/service/ipc.go +++ b/service/ziti-tunnel/service/ipc.go @@ -119,9 +119,7 @@ func SubMain(ops chan string, changes chan<- svc.Status) error { log.Debug("shutting down. start a ZitiDump") for _, id := range rts.ids { if id.CId != nil { - sb := strings.Builder{} - cziti.ZitiDumpOnShutdown(id.CId, &sb) - log.Infof("working around the c sdk's limitation of embedding newlines on calling ziti_shutdown\n %s", sb.String()) + cziti.ZitiDumpOnShutdown(id.CId) } } log.Debug("shutting down. ZitiDump complete") @@ -129,9 +127,9 @@ func SubMain(ops chan string, changes chan<- svc.Status) error { requestShutdown("service shutdown") // signal to any connected consumers that the service is shutting down normally - events.broadcast <- dto.StatusEvent{ + rts.BroadcastEvent(dto.StatusEvent{ Op: "shutdown", - } + }) // wait 1 second for the shutdown to send to clients shutdownDelay := make(chan bool) @@ -282,7 +280,7 @@ func initialize(cLogLevel int) error { dnsIpAsUint32 := binary.BigEndian.Uint32(ipnet.IP) cziti.InitTunnelerDns(dnsIpAsUint32, len(ipnet.Mask)) - assignedIp, t, err := rts.CreateTun(rts.state.TunIpv4, rts.state.TunIpv4Mask) + assignedIp, t, err := rts.CreateTun(rts.state.TunIpv4, rts.state.TunIpv4Mask, rts.state.AddDns) if err != nil { return err } @@ -681,8 +679,9 @@ func serveEvents(conn net.Conn) { log.Debugf("accepting a new client for serveEvents. total connection count: %d", eventsConnections) consumer := make(chan interface{}, 8) - events.register(randomInt, consumer) - defer events.unregister(randomInt) + id := fmt.Sprintf("serveEvents:%d", randomInt) + events.register(id, consumer) + defer events.unregister(id) w := bufio.NewWriter(conn) o := json.NewEncoder(w) @@ -704,12 +703,18 @@ loop: for { select { case msg := <-consumer: - err := o.Encode(msg) - if err != nil { - log.Debugf("exiting from serveEvents - %v", err) + log.Tracef("sending event to id: %s [%v]", id, msg) + eerr := o.Encode(msg) + if eerr != nil { + log.Warnf("exiting from serveEvents due to error: %v", eerr) break loop } - _ = w.Flush() + ferr := w.Flush() + if ferr != nil { + log.Warnf("flush error: %v", ferr) + return + } + log.Tracef("sent event to id: %s [%v]", id, msg) case <-interrupt: break loop } @@ -717,6 +722,10 @@ loop: log.Info("a connected event client has disconnected") } +func writerFlush(writer bufio.Writer) { + writer.Flush() +} + func reportStatus(out *json.Encoder) { s := rts.ToStatus(true) respond(out, dto.ZitiTunnelStatus{ @@ -901,10 +910,10 @@ func connectIdentity(id *Id) { return true }) - events.broadcast <- dto.IdentityEvent{ + rts.BroadcastEvent(dto.IdentityEvent{ ActionEvent: dto.IDENTITY_ADDED, Id: id.Identity, - } + }) log.Infof("connecting identity completed: %s[%s] %t/%t", id.Name, id.FingerPrint, id.MfaEnabled, id.MfaNeeded) } } @@ -1003,15 +1012,50 @@ func acceptServices() { select { case <-shutdown: return - case serviceChange := <-cziti.ServiceChanges: - log.Debugf("processing service change event. id:%s name:%s", serviceChange.Service.Id, serviceChange.Service.Name) - change := dto.ServiceEvent(serviceChange) - events.broadcast <- change - log.Debugf("dispatched %s service change event", serviceChange.Op) + case bulkServiceChange := <-cziti.BulkServiceChanges: + log.Debugf("processing a bulk service change event. Hostnames to add/remove:[%d/%d] service notifications added/removed: [%d/%d]", + len(bulkServiceChange.HostnamesToAdd), + len(bulkServiceChange.HostnamesToRemove), + len(bulkServiceChange.ServicesToAdd), + len(bulkServiceChange.ServicesToRemove)) + handleBulkServiceChange(bulkServiceChange) } } } +func handleBulkServiceChange(sc cziti.BulkServiceChange) { + if len(sc.HostnamesToRemove) > 0 { + log.Debug("removing rules from NRPT") + windns.RemoveNrptRules(sc.HostnamesToRemove) + log.Info("removed NRPT rules for: %v", sc.HostnamesToRemove) + } else { + log.Debug("bulk service change had no hostnames to remove") + } + + if len(sc.HostnamesToAdd) > 0 { + log.Debug("adding rules to NRPT") + windns.AddNrptRules(sc.HostnamesToAdd, rts.state.TunIpv4) + log.Infof("mapped the following hostnames: %v", sc.HostnamesToAdd) + } + + be := dto.BulkServiceEvent{ + ActionEvent: dto.SERVICE_BULK, + Fingerprint: sc.Fingerprint, + AddedServices: sc.ServicesToAdd, + RemovedServices: sc.ServicesToRemove, + } + + rts.BroadcastEvent(be) + + var m = dto.IdentityEvent{ + ActionEvent: dto.IdentityUpdateComplete, + Id: dto.Identity{ + FingerPrint: sc.Fingerprint, + }, + } + rts.BroadcastEvent(m) +} + func handleEvents(isInitialized chan struct{}) { events.run() d := 5 * time.Second @@ -1027,10 +1071,10 @@ func handleEvents(isInitialized chan struct{}) { case <-every5s.C: s := rts.ToMetrics() - events.broadcast <- dto.MetricsEvent{ + rts.BroadcastEvent(dto.MetricsEvent{ StatusEvent: dto.StatusEvent{Op: "metrics"}, Identities: s.Identities, - } + }) } } } @@ -1098,10 +1142,10 @@ func authMfa(out *json.Encoder, fingerprint string, code string) { // when the log level is updated through command line, the message is broadcasted to UI and update service as well func sendLogLevelAndNotify(enc *json.Encoder, loglevel string) { - events.broadcast <- dto.LogLevelEvent{ + rts.BroadcastEvent(dto.LogLevelEvent{ ActionEvent: dto.LOGLEVEL_CHANGED, LogLevel: loglevel, - } + }) message := fmt.Sprintf("Loglevel %s is sent to the events channel", loglevel) log.Info(message) resp := dto.Response{Message: "success", Code: SUCCESS, Error: "", Payload: message} @@ -1112,10 +1156,10 @@ func sendLogLevelAndNotify(enc *json.Encoder, loglevel string) { func sendIdentityAndNotifyUI(enc *json.Encoder, fingerprint string) { for _, id := range rts.ids { if id.FingerPrint == fingerprint { - events.broadcast <- dto.IdentityEvent{ + rts.BroadcastEvent(dto.IdentityEvent{ ActionEvent: dto.IDENTITY_ADDED, Id: id.Identity, - } + }) message := fmt.Sprintf("Identity %s - %s updated message is sent to the events channel", id.Identity.Name, id.Identity.FingerPrint) log.Info(message) resp := dto.Response{Message: "success", Code: SUCCESS, Error: "", Payload: message} diff --git a/service/ziti-tunnel/service/state.go b/service/ziti-tunnel/service/state.go index 26933a70..cb5b06a3 100644 --- a/service/ziti-tunnel/service/state.go +++ b/service/ziti-tunnel/service/state.go @@ -21,6 +21,7 @@ import ( "bufio" "encoding/json" "fmt" + "golang.org/x/sys/windows" "io" "io/ioutil" "net" @@ -121,6 +122,7 @@ func (t *RuntimeState) ToStatus(onlyInitialized bool) dto.TunnelStatus { ServiceVersion: Version, TunIpv4: t.state.TunIpv4, TunIpv4Mask: t.state.TunIpv4Mask, + AddDns: t.state.AddDns, } i := 0 @@ -162,7 +164,7 @@ func (t *RuntimeState) ToMetrics() dto.TunnelStatus { return clean } -func (t *RuntimeState) CreateTun(ipv4 string, ipv4mask int) (net.IP, *tun.Device, error) { +func (t *RuntimeState) CreateTun(ipv4 string, ipv4mask int, applyDns bool) (net.IP, *tun.Device, error) { log.Infof("creating TUN device: %s", TunName) tunDevice, err := tun.CreateTUN(TunName, 64*1024-1) if err == nil { @@ -219,6 +221,11 @@ func (t *RuntimeState) CreateTun(ipv4 string, ipv4mask int) (net.IP, *tun.Device } log.Info("routing applied") + if applyDns { + //for windows 10+, could 'domains' be able to replace NRPT? dunno - didn't test it + luid.SetDNS(windows.AF_INET, []net.IP{ip}, nil) + } + return ip, t.tun, nil } @@ -262,10 +269,10 @@ func (t *RuntimeState) LoadIdentity(id *Id, refreshInterval int) { id.MfaEnabled = id.CId.MfaEnabled id.MfaNeeded = id.CId.MfaNeeded - events.broadcast <- dto.IdentityEvent{ + rts.BroadcastEvent(dto.IdentityEvent{ ActionEvent: dto.IDENTITY_ADDED, Id: id.Identity, - } + }) log.Infof("connecting identity completed: %s[%s] %t/%t", id.Name, id.FingerPrint, id.MfaEnabled, id.MfaNeeded) } @@ -477,6 +484,9 @@ func (t *RuntimeState) ReleaseIP() { } func (t *RuntimeState) BroadcastEvent(event interface{}) { + if len(events.broadcast) == cap(events.broadcast) { + log.Warn("event channel is full and is about to block!") + } events.broadcast <- event } diff --git a/service/ziti-tunnel/service/topic.go b/service/ziti-tunnel/service/topic.go index c6d95897..beb5dbd5 100644 --- a/service/ziti-tunnel/service/topic.go +++ b/service/ziti-tunnel/service/topic.go @@ -19,23 +19,23 @@ package service type topic struct { broadcast chan interface{} - channels map[int]chan interface{} + channels map[string]chan interface{} done chan bool } func newTopic(cap int16) topic { return topic{ broadcast: make(chan interface{}, cap), - channels: make(map[int]chan interface{}, cap), + channels: make(map[string]chan interface{}, cap), done: make(chan bool, cap), } } -func (t *topic) register(id int, c chan interface{}) { +func (t *topic) register(id string, c chan interface{}) { t.channels[id] = c } -func (t *topic) unregister(id int) { +func (t *topic) unregister(id string) { delete(t.channels, id) } @@ -48,7 +48,10 @@ func (t *topic) run() { for { select { case msg := <-t.broadcast: - for _, c := range t.channels { + for id, c := range t.channels { + if len(c) == cap(c) { + log.Warnf("channel with id [%s] is about to block!", id) + } c <- msg } break diff --git a/service/ziti-tunnel/version.go b/service/ziti-tunnel/version.go index f80071c9..3fed4615 100644 --- a/service/ziti-tunnel/version.go +++ b/service/ziti-tunnel/version.go @@ -20,9 +20,9 @@ package main const ( - Version = "1.9.2" - Revision = "4e9875920983" - Branch = "main" + Version = "1.9.3" + Revision = "b6361cdb1b8d" + Branch = "release-next" BuildUser = "" - BuildDate = "2021-04-08 11:05:00" + BuildDate = "2021-04-12 17:20:21" ) diff --git a/version b/version index 8fdcf386..77fee73a 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.9.2 +1.9.3