Skip to content

Commit 420cb14

Browse files
committed
Feature: Hosts file editor
1 parent 39d6956 commit 420cb14

File tree

18 files changed

+1736
-1286
lines changed

18 files changed

+1736
-1286
lines changed

Source/NETworkManager.Localization/Resources/Strings.Designer.cs

Lines changed: 1294 additions & 1268 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Source/NETworkManager.Localization/Resources/Strings.resx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3879,7 +3879,13 @@ Right-click for more options.</value>
38793879
<data name="ProfileFile" xml:space="preserve">
38803880
<value>Profile file</value>
38813881
</data>
3882-
<data name="ApplicationName_HostsEditor" xml:space="preserve">
3883-
<value>Hosts Editor</value>
3882+
<data name="ApplicationName_HostsFileEditor" xml:space="preserve">
3883+
<value>Hosts File Editor</value>
3884+
</data>
3885+
<data name="HostsFileEditor" xml:space="preserve">
3886+
<value>Hosts File Editor</value>
3887+
</data>
3888+
<data name="HostsFileEditorAdminMessage" xml:space="preserve">
3889+
<value>To edit the hosts file, the application must be started with elevated rights!</value>
38843890
</data>
38853891
</root>

Source/NETworkManager.Models/AWS/AWSProfile.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ public static class AWSProfile
66
{
77
public static List<AWSProfileInfo> GetDefaultList()
88
{
9-
return new List<AWSProfileInfo>
10-
{
11-
new(false, "default", "eu-central-1"),
12-
new(false, "default", "us-east-1")
13-
};
9+
return
10+
[
11+
new AWSProfileInfo(false, "default", "eu-central-1"),
12+
new AWSProfileInfo(false, "default", "us-east-1")
13+
];
1414
}
1515
}

Source/NETworkManager.Models/AWS/AWSProfileInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
namespace NETworkManager.Models.AWS;
22

33
/// <summary>
4-
/// Class is used to store informations about an AWS profile.
4+
/// Class is used to store information about an AWS profile.
55
/// </summary>
66
public class AWSProfileInfo
77
{
@@ -15,7 +15,7 @@ public AWSProfileInfo()
1515
/// <summary>
1616
/// Create an instance of <see cref="AWSProfileInfo" /> with parameters.
1717
/// </summary>
18-
/// <param name="IsEnabled"><see cref="IsEnabled" />.</param>
18+
/// <param name="isEnabled"><see cref="IsEnabled" />.</param>
1919
/// <param name="profile"><see cref="Profile" />.</param>
2020
/// <param name="region"><see cref="Region" />.</param>
2121
public AWSProfileInfo(bool isEnabled, string profile, string region)

Source/NETworkManager.Models/ApplicationManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public static Canvas GetIcon(ApplicationName name)
9292
case ApplicationName.SNTPLookup:
9393
canvas.Children.Add(new PackIconMaterial { Kind = PackIconMaterialKind.ClockCheckOutline });
9494
break;
95-
case ApplicationName.HostsEditor:
95+
case ApplicationName.HostsFileEditor:
9696
canvas.Children.Add(new PackIconMaterial { Kind = PackIconMaterialKind.FileEditOutline });
9797
break;
9898
case ApplicationName.DiscoveryProtocol:

Source/NETworkManager.Models/ApplicationName.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,9 @@ public enum ApplicationName
9191
SNTPLookup,
9292

9393
/// <summary>
94-
/// Hosts editor application.
94+
/// Hosts file editor application.
9595
/// </summary>
96-
HostsEditor,
96+
HostsFileEditor,
9797

9898
/// <summary>
9999
/// Discovery protocol application.
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Text.RegularExpressions;
5+
using log4net;
6+
using NETworkManager.Utilities;
7+
8+
namespace NETworkManager.Models.HostsFileEditor;
9+
10+
public class HostsFileEditor
11+
{
12+
#region Events
13+
public event EventHandler HostsFileChanged;
14+
15+
private void OnHostsFileChanged()
16+
{
17+
Log.Debug("OnHostsFileChanged - Hosts file changed.");
18+
HostsFileChanged?.Invoke(this, EventArgs.Empty);
19+
}
20+
#endregion
21+
22+
#region Variables
23+
private static readonly ILog Log = LogManager.GetLogger(typeof(HostsFileEditor));
24+
25+
/// <summary>
26+
/// Path to the hosts file.
27+
/// </summary>
28+
private static string HostsFilePath => Path.Combine(Environment.SystemDirectory, "drivers", "etc", "hosts");
29+
30+
/// <summary>
31+
/// Regex to match a hosts file entry with optional comments, supporting IPv4, IPv6, and hostnames
32+
/// </summary>
33+
private readonly Regex _hostsFileEntryRegex = new Regex(RegexHelper.HostsEntryRegex);
34+
35+
#endregion
36+
37+
#region Constructor
38+
public HostsFileEditor()
39+
{
40+
// Create a file system watcher to monitor changes to the hosts file
41+
try
42+
{
43+
Log.Debug("HostsFileEditor - Creating file system watcher for hosts file...");
44+
45+
FileSystemWatcher watcher = new();
46+
watcher.Path = Path.GetDirectoryName(HostsFilePath) ?? throw new InvalidOperationException("Hosts file path is invalid.");
47+
watcher.Filter = Path.GetFileName(HostsFilePath) ?? throw new InvalidOperationException("Hosts file name is invalid.");
48+
watcher.NotifyFilter = NotifyFilters.LastWrite;
49+
50+
// Maybe fired twice. This is a known bug/feature.
51+
// See: https://stackoverflow.com/questions/1764809/filesystemwatcher-changed-event-is-raised-twice
52+
watcher.Changed += (_, _) => OnHostsFileChanged();
53+
54+
watcher.EnableRaisingEvents = true;
55+
56+
Log.Debug("HostsFileEditor - File system watcher for hosts file created.");
57+
}
58+
catch (Exception ex)
59+
{
60+
Log.Error("Failed to create file system watcher for hosts file.", ex);
61+
}
62+
}
63+
#endregion
64+
65+
#region Methods
66+
/// <summary>
67+
///
68+
/// </summary>
69+
/// <returns></returns>
70+
public IEnumerable<HostsFileEntry> GetHostsFileEntries()
71+
{
72+
var hostsFileLines = File.ReadAllLines(HostsFilePath);
73+
74+
// Parse the hosts file content
75+
var entries = new List<HostsFileEntry>();
76+
77+
foreach (var line in hostsFileLines)
78+
{
79+
var result = _hostsFileEntryRegex.Match(line.Trim());
80+
81+
if (result.Success)
82+
{
83+
Log.Debug("GetHostsFileEntries - Line matched: " + line);
84+
85+
var entry = new HostsFileEntry
86+
{
87+
IsEnabled = !result.Groups[1].Value.Equals("#"),
88+
IpAddress = result.Groups[2].Value,
89+
HostName = result.Groups[3].Value.Replace(@"\s", "").Split([' ']),
90+
Comment = result.Groups[4].Value,
91+
Line = line
92+
};
93+
94+
// Skip example entries
95+
if(!entry.IsEnabled && entry.IpAddress is "102.54.94.97" or "38.25.63.10" && entry.HostName[0] is "rhino.acme.com" or "x.acme.com")
96+
{
97+
Log.Debug("GetHostsFileEntries - Matched example entry. Skipping...");
98+
continue;
99+
}
100+
101+
entries.Add(entry);
102+
}
103+
else
104+
{
105+
Log.Debug("GetHostsFileEntries - Line not matched: " + line);
106+
}
107+
}
108+
109+
return entries;
110+
}
111+
#endregion
112+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
namespace NETworkManager.Models.HostsFileEditor;
2+
3+
/// <summary>
4+
/// Class that represents a single entry in the hosts file.
5+
/// </summary>
6+
public class HostsFileEntry
7+
{
8+
/// <summary>
9+
/// Indicates whether the entry is enabled or not.
10+
/// </summary>
11+
public bool IsEnabled { get; init; }
12+
13+
/// <summary>
14+
/// IP address of the host.
15+
/// </summary>
16+
public string IpAddress { get; init; }
17+
18+
/// <summary>
19+
/// Host name(s) of the host.
20+
/// </summary>
21+
public string[] HostName { get; init; }
22+
23+
/// <summary>
24+
/// Comment of the host.
25+
/// </summary>
26+
public string Comment { get; init; }
27+
28+
/// <summary>
29+
/// Line of the entry in the hosts file.
30+
/// </summary>
31+
public string Line { get; init; }
32+
33+
/// <summary>
34+
/// Creates a new instance of <see cref="HostsFileEntry" />.
35+
/// </summary>
36+
public HostsFileEntry()
37+
{
38+
39+
}
40+
41+
/// <summary>
42+
/// Creates a new instance of <see cref="HostsFileEntry" /> with parameters.
43+
/// </summary>
44+
/// <param name="isEnabled">Indicates whether the entry is enabled or not.</param>
45+
/// <param name="ipAddress">IP address of the host.</param>
46+
/// <param name="hostName">Host name(s) of the host.</param>
47+
/// <param name="comment">Comment of the host.</param>
48+
public HostsFileEntry(bool isEnabled, string ipAddress, string[] hostName, string comment)
49+
{
50+
IsEnabled = isEnabled;
51+
IpAddress = ipAddress;
52+
HostName = hostName;
53+
Comment = comment;
54+
}
55+
56+
/// <summary>
57+
/// Creates a new instance of <see cref="HostsFileEntry" /> with parameters.
58+
/// </summary>
59+
/// <param name="isEnabled">Indicates whether the entry is enabled or not.</param>
60+
/// <param name="ipAddress">IP address of the host.</param>
61+
/// <param name="hostName">Host name(s) of the host.</param>
62+
/// <param name="comment">Comment of the host.</param>
63+
/// <param name="line">Line of the entry in the hosts file.</param>
64+
public HostsFileEntry(bool isEnabled, string ipAddress, string[] hostName, string comment, string line) : this(isEnabled, ipAddress, hostName, comment)
65+
{
66+
Line = line;
67+
}
68+
}

Source/NETworkManager.Models/Lookup/OUILookup.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public static class OUILookup
2222
/// </summary>
2323
static OUILookup()
2424
{
25-
OUIInfoList = new List<OUIInfo>();
25+
OUIInfoList = [];
2626

2727
var document = new XmlDocument();
2828
document.Load(OuiFilePath);

Source/NETworkManager.Models/Lookup/PortLookup.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public static class PortLookup
1919
/// </summary>
2020
static PortLookup()
2121
{
22-
PortList = new List<PortLookupInfo>();
22+
PortList = [];
2323

2424
var document = new XmlDocument();
2525
document.Load(PortsFilePath);

0 commit comments

Comments
 (0)