Skip to content

Commit

Permalink
Avoided a deadlock in DNS resolution.
Browse files Browse the repository at this point in the history
Minor refactoring and warnings resolutions.
  • Loading branch information
wokhan committed Mar 18, 2023
1 parent 40f9fcb commit 214f856
Show file tree
Hide file tree
Showing 15 changed files with 134 additions and 68 deletions.
1 change: 0 additions & 1 deletion Common/IO/Files/PathResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

using Wokhan.WindowsFirewallNotifier.Common.Logging;

namespace Wokhan.WindowsFirewallNotifier.Common.IO.Files;
Expand Down
56 changes: 40 additions & 16 deletions Common/Net/DNS/ResolvedIPInformation.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
using System;
using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;

using Wokhan.WindowsFirewallNotifier.Common.Logging;

namespace Wokhan.WindowsFirewallNotifier.Common.Net.DNS;

public class ResolvedIPInformation
public class ResolvedIPInformation : IDisposable
{
internal static readonly ConcurrentDictionary<string, ResolvedIPInformation> CachedIPHostEntryDict = new();

private readonly ManualResetEventSlim _eventSlim = new ManualResetEventSlim(true);
private readonly ManualResetEventSlim _eventSlim = new(true);

private readonly string ip;

Expand All @@ -27,40 +28,63 @@ public class ResolvedIPInformation
return String.Empty;
}

var entry = CachedIPHostEntryDict.GetOrAdd(ip, sourceIp => new ResolvedIPInformation(sourceIp));
// If using GetHostEntryAsync in WaitOrResolveHost, SocketExceptions are never catched back and a deadlock occurs...
// So we've to initiate the async task ourselves.
return await Task.Run(() =>
{
var entry = CachedIPHostEntryDict.GetOrAdd(ip, sourceIp => new ResolvedIPInformation(sourceIp));

// Ensure that it has been resolved
return await entry.WaitOrResolveHostAsync();
if (entry.handled)
{
return entry.resolvedHost;
}

// Ensure that it has been resolved
return entry.WaitOrResolveHost();
});
}

public ResolvedIPInformation(string ip)
{
this.ip = ip;
}

private async Task<string> WaitOrResolveHostAsync()
private string WaitOrResolveHost()
{
_eventSlim.Wait();

if (!handled)
try
{
_eventSlim.Reset();
_eventSlim.Wait();

try
if (!handled)
{
resolvedHost = (await Dns.GetHostEntryAsync(ip)).HostName;
}
catch (Exception e)
{
LogHelper.Debug($"Unable to resolve {ip}, message was {e.Message}");
_eventSlim.Reset();

resolvedHost = Dns.GetHostEntry(ip)?.HostName ?? "N/A";
}
}
catch (SocketException se)
{
resolvedHost = se.Message;
}
catch (Exception e)
{
LogHelper.Debug($"Unable to resolve {ip}, message was {e.Message}");
}
finally
{

handled = true;

// Releases all pending entry resolutions in other threads for this entry
// TODO: _eventSlim might need to be disposed once the hostname resolution is done and after no thread is waiting... Could induce an unjustified memory use if noe?
_eventSlim.Set();
}

return resolvedHost;
}

public void Dispose()
{
_eventSlim.Dispose();
}
}
14 changes: 12 additions & 2 deletions Common/Processes/ProcessHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -644,17 +644,27 @@ public static void StartOrRestoreToForeground(ProcessNames processName)
/// Opens Windows explorer and selects the file targeted by "flepath"
/// </summary>
/// <param name="filepath">Full path to the file to select</param>
public static void BrowseToFile(string filepath)
public static void BrowseToFile(string? filepath)
{
if (filepath is null)
{
return;
}

StartShellExecutable(ProcessNames.Explorer.FileName, $"/select,{filepath}", true);
}

/// <summary>
/// Opens a folder in Windows explorer.
/// </summary>
/// <param name="folderPath">Path to the folder</param>
public static void OpenFolder(string folderPath)
public static void OpenFolder(string? folderPath)
{
if (folderPath is null)
{
return;
}

StartShellExecutable(ProcessNames.Explorer.FileName, folderPath, true);
}

Expand Down
1 change: 0 additions & 1 deletion Common/UI/ViewModels/ConnectionBaseInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ public string? TargetHostName
public int RawProtocol { get; protected set; }
public string? Protocol { get; protected set; }
public string? Direction { get; protected set; }
public string? FilterId { get; protected set; }

private bool fileInfoResolutionTriggered;

Expand Down
2 changes: 1 addition & 1 deletion Common/UI/ViewModels/LogEntryViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public class LogEntryViewModel : ConnectionBaseInfo
return false;
}

private static string GetReplacementString(EventLogEntry entry, int i)
private static string? GetReplacementString(EventLogEntry entry, int i)
{
return entry.ReplacementStrings.DefaultIfEmpty(string.Empty).ElementAtOrDefault(i);
}
Expand Down
2 changes: 1 addition & 1 deletion Console/UI/Controls/BandwidthGraph.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ private void InitAxes()

public double CurrentStart
{
get => (double)(xAxis is not null ? xAxis.MinLimit + (xAxis.MaxLimit - xAxis.MinLimit) / 2 : 0);
get => (xAxis is not null ? xAxis.MinLimit + (xAxis.MaxLimit - xAxis.MinLimit) / 2 : 0) ?? 0;
set
{
if (isXPanned)
Expand Down
8 changes: 4 additions & 4 deletions Console/UI/Controls/Map.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ public ObservableCollection<Connection> Connections
public bool IsAerial
{
get => Mode is AerialMode;
set { Mode = (value ? new AerialMode(true) : (MapMode)new RoadMode()); }
set { Mode = (value ? new AerialMode(true) : new RoadMode()); }
}

[ObservableProperty]
[NotifyPropertyChangedFor(nameof(IsAerial))]
private MapMode _mode = new RoadMode();

public ObservableCollection<GeoConnection2> ConnectionsRoutes { get; } = new ObservableCollection<GeoConnection2>();
public ObservableCollection<GeoConnection2> ConnectionsRoutes { get; } = new();

public Map()
{
Expand Down Expand Up @@ -102,7 +102,7 @@ async void Map_Loaded(object sender, RoutedEventArgs e)
ProgressStack.Visibility = Visibility.Collapsed;
}

private void GeoWatcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
private void GeoWatcher_PositionChanged(object? sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
if (!geoWatcher.Position.Location.IsUnknown)
{
Expand Down Expand Up @@ -142,7 +142,7 @@ public void UpdateMap()
}

//TODO: Temporary check for addresses validity (for mapping purpose only). Doesn't look like the right way to do this.
private bool IsValid(string remoteAddress)
private bool IsValid(string? remoteAddress)
{
return (!String.IsNullOrEmpty(remoteAddress)
&& remoteAddress != "127.0.0.1"
Expand Down
8 changes: 4 additions & 4 deletions Console/UI/Pages/AdapterInfo.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@
<TextBlock Grid.Row="1" Text="Interface type" />
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Information.NetworkInterfaceType}" />
<TextBlock Grid.Row="2" Text="Speed" />
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Information.Speed}" />
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Information.Speed,Converter={StaticResource unitFormatter},ConverterParameter='ps'}" />
<TextBlock Grid.Row="3" Text="MAC address" />
<TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding MAC}" />
<TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding MAC,Mode=OneTime}" />
</Grid>
</Border>
<Border Style="{StaticResource Card}">
Expand Down Expand Up @@ -141,15 +141,15 @@
<Border Style="{StaticResource Card}">
<StackPanel>
<Label FontWeight="Bold" Content="IP addresses" />
<DataGrid GridLinesVisibility="None" Background="Transparent" AutoGenerateColumns="False" BorderThickness="0" HeadersVisibility="Column" ColumnWidth="*" IsReadOnly="True" ItemsSource="{Binding Properties.UnicastAddresses,Mode=OneWay}">
<DataGrid IsHitTestVisible="False" GridLinesVisibility="None" Background="Transparent" AutoGenerateColumns="False" BorderThickness="0" HeadersVisibility="Column" ColumnWidth="*" IsReadOnly="True" ItemsSource="{Binding Properties.UnicastAddresses,Mode=OneWay}">
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="5" />
</Style>
</DataGrid.CellStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="Address" Binding="{Binding Address,Mode=OneWay}" />
<DataGridTextColumn Header="Address" Binding="{Binding Address,Mode=OneTime}" />
<DataGridTextColumn Header="Preferred lifetime" Binding="{Binding AddressPreferredLifetime,Mode=OneWay,Converter={StaticResource secondsConverter}}" />
<DataGridTextColumn Header="Valid lifetime" Binding="{Binding AddressValidLifetime,Mode=OneWay,Converter={StaticResource secondsConverter}}" />
<DataGridTextColumn Header="DHCP lease" Binding="{Binding DhcpLeaseLifetime,Mode=OneWay,Converter={StaticResource secondsConverter}}" />
Expand Down
43 changes: 40 additions & 3 deletions Console/UI/Pages/Connections.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:controls="clr-namespace:Wokhan.WindowsFirewallNotifier.Console.UI.Controls"
xmlns:ext="clr-namespace:Wokhan.UI.Xaml.Extensibility;assembly=Wokhan.UI"
xmlns:vm="clr-namespace:Wokhan.WindowsFirewallNotifier.Common.UI.ViewModels;assembly=Wokhan.WindowsFirewallNotifier.Common"
xmlns:settings="clr-namespace:Wokhan.WindowsFirewallNotifier.Common.Config;assembly=Wokhan.WindowsFirewallNotifier.Common"
DataContext="{Binding RelativeSource={x:Static RelativeSource.Self}}"
xmlns:dummydata="clr-namespace:Wokhan.WindowsFirewallNotifier.Console.ViewModels" xmlns:componentmodel="clr-namespace:System.ComponentModel;assembly=WindowsBase"
Expand Down Expand Up @@ -144,8 +143,46 @@
<DataGridTextColumn Header="Remote port" Binding="{Binding TargetPort, Mode=OneTime}" />
<DataGridTextColumn Header="State" Binding="{Binding State, Mode=OneWay}" />
<DataGridTextColumn Header="Creation" Binding="{Binding CreationTime, Mode=OneTime}" />
<DataGridTextColumn Header="Up" Binding="{Binding OutboundBandwidth, Mode=OneWay, Converter={StaticResource unitFormatter}, ConverterParameter='ps'}" />
<DataGridTextColumn Header="Down" Binding="{Binding InboundBandwidth, Mode=OneWay, Converter={StaticResource unitFormatter}, ConverterParameter='ps'}" />
<DataGridTemplateColumn Header="Up">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ContentControl Content="{StaticResource Image_DataUp}">
<ContentControl.Resources>
<Style TargetType="Path">
<Style.Triggers>
<DataTrigger Binding="{Binding OutboundBandwidth}" Value="0">
<Setter Property="Fill" Value="Gray" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Resources>
</ContentControl>
<TextBlock Text="{Binding OutboundBandwidth, Mode=OneWay, Converter={StaticResource unitFormatter}, ConverterParameter='ps'}" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Down">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ContentControl Content="{StaticResource Image_DataDown}">
<ContentControl.Resources>
<Style TargetType="Path">
<Style.Triggers>
<DataTrigger Binding="{Binding InboundBandwidth}" Value="0">
<Setter Property="Fill" Value="Gray" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Resources>
</ContentControl>
<TextBlock Text="{Binding InboundBandwidth, Mode=OneWay, Converter={StaticResource unitFormatter}, ConverterParameter='ps'}" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!--<DataGridTextColumn Header="FirewallRule" Binding="{Binding FirewallRule.Count, Mode=OneWay}" />-->
</DataGrid.Columns>
</DataGrid>
Expand Down
2 changes: 1 addition & 1 deletion Console/UI/Pages/Rules.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ private void RemoveRule()
[RelayCommand(CanExecute = nameof(LocateCanExecute))]
private void Locate()
{
ProcessHelper.BrowseToFile(SelectedItem.ApplicationName);
ProcessHelper.BrowseToFile(SelectedItem!.ApplicationName);
}

public bool LocateCanExecute => SelectedItem is not null;
Expand Down
2 changes: 1 addition & 1 deletion Console/UI/Pages/TimerBasedPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Wokhan.WindowsFirewallNotifier.Console.UI.Pages;

public class TimerBasedPage : Page, INotifyPropertyChanged
{
private Timer timer;
private readonly Timer timer;

public virtual List<double> Intervals { get; } = new List<double> { 0.5, 1, 5, 10 };

Expand Down
4 changes: 2 additions & 2 deletions Console/UI/Windows/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@
</TabItem>
</TabControl>
</Grid>
<StatusBar Grid.Row="2" Height="30" Background="Transparent">
<TextBlock Text="{Binding ConnectionsCount,StringFormat='{}{0} active connections'}" />
<StatusBar Margin="10,0" Grid.Row="2" Height="30" Background="Transparent">
<TextBlock Text="{Binding ConnectionsCount,StringFormat='{}{0} active connection(s)'}" />
</StatusBar>
</Grid>
</Window>
22 changes: 11 additions & 11 deletions Console/UI/Windows/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
using CommunityToolkit.Mvvm.ComponentModel;

using Microsoft.Win32;

using System;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Timers;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;

using Wokhan.WindowsFirewallNotifier.Common.Logging;
using Wokhan.WindowsFirewallNotifier.Common.Net.IP;
using Wokhan.WindowsFirewallNotifier.Console.UI.Pages;

namespace Wokhan.WindowsFirewallNotifier.Console.UI.Windows;

[ObservableObject]
public partial class MainWindow
public partial class MainWindow : Window
{
private Timer timer;

[ObservableProperty]
private int connectionsCount;

Expand All @@ -43,19 +44,18 @@ public MainWindow()
{
InitializeComponent();

var timer = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(1), IsEnabled = true };
timer.Tick += Timer_Tick;
timer = new Timer(Timer_Tick, null, 1000, 1000);

AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
Application.Current.DispatcherUnhandledException += Current_DispatcherUnhandledException;
}

private void Timer_Tick(object? sender, EventArgs e)
private void Timer_Tick(object? _)
{
ConnectionsCount = IPHelper.GetAllConnections().Count();
}

void Current_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
void Current_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
if (e.Exception is InvalidOperationException && e.Exception.InnerException is not null && e.Exception.InnerException is Win32Exception win32exception && win32exception.NativeErrorCode == ERROR_PRIVILEGE_NOT_HELD)
{
Expand All @@ -69,9 +69,9 @@ void Current_DispatcherUnhandledException(object sender, System.Windows.Threadin
e.Handled = true;
}

private async void ForceDialog(string p1, string p2)
private void ForceDialog(string p1, string p2)
{
//MessageBox.Show(p1, p2);
MessageBox.Show(p1, p2);
/*var dial = await this.GetCurrentDialogAsync<BaseMetroDialog>();
if (dial is not null)
Expand Down
4 changes: 2 additions & 2 deletions Console/ViewModels/ExposedInterfaceView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Wokhan.WindowsFirewallNotifier.Console.ViewModels;

public partial class ExposedInterfaceView : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public event PropertyChangedEventHandler? PropertyChanged;

public void NotifyPropertyChanged(string propertyName)
{
Expand All @@ -16,7 +16,7 @@ public void NotifyPropertyChanged(string propertyName)

public string MAC => String.Join(":", Information.GetPhysicalAddress().GetAddressBytes().Select(b => b.ToString("X2")));

public NetworkInterface Information { get; private set; }
public NetworkInterface? Information { get; private set; }

public IPInterfaceStatistics Statistics => Information.GetIPStatistics();

Expand Down
Loading

0 comments on commit 214f856

Please sign in to comment.