mirror of
https://github.com/alexhopeoconnor/dnsmasq-webui.git
synced 2026-10-03 22:12:00 +10:00
Fix UTF-8 BOM in dnsmasq config/hosts writes so dnsmasq does not fail
- Add DnsmasqFileEncoding.Utf8NoBom for all config and hosts file writes - Strip BOM from first line when reading main config and managed config - Prevents 'bad option at line 1' when dnsmasq loads files written by the app
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
using System.Text;
|
||||
|
||||
namespace DnsmasqWebUI.Infrastructure;
|
||||
|
||||
/// <summary>Encoding for dnsmasq config and hosts files. UTF-8 without BOM so dnsmasq does not see a BOM as "bad option" on line 1.</summary>
|
||||
public static class DnsmasqFileEncoding
|
||||
{
|
||||
public static readonly Encoding Utf8NoBom = new UTF8Encoding(false);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Text;
|
||||
using DnsmasqWebUI.Infrastructure;
|
||||
using DnsmasqWebUI.Models.Config;
|
||||
using DnsmasqWebUI.Models.Dhcp;
|
||||
using DnsmasqWebUI.Models.EffectiveConfig;
|
||||
@@ -82,7 +83,7 @@ public class DnsmasqConfigService : IDnsmasqConfigService
|
||||
var dir = Path.GetDirectoryName(managedHostsPath);
|
||||
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
|
||||
Directory.CreateDirectory(dir);
|
||||
File.WriteAllText(managedHostsPath, "");
|
||||
File.WriteAllText(managedHostsPath, "", DnsmasqFileEncoding.Utf8NoBom);
|
||||
}
|
||||
|
||||
public async Task WriteDhcpHostsAsync(IReadOnlyList<DhcpHostEntry> entries, CancellationToken ct = default)
|
||||
@@ -110,7 +111,10 @@ public class DnsmasqConfigService : IDnsmasqConfigService
|
||||
|
||||
IReadOnlyList<string> rawLines;
|
||||
if (File.Exists(path))
|
||||
rawLines = await File.ReadAllLinesAsync(path, Encoding.UTF8, ct);
|
||||
{
|
||||
var read = await File.ReadAllLinesAsync(path, DnsmasqFileEncoding.Utf8NoBom, ct);
|
||||
rawLines = StripBomFromFirstLine(read);
|
||||
}
|
||||
else
|
||||
rawLines = Array.Empty<string>();
|
||||
|
||||
@@ -136,7 +140,7 @@ public class DnsmasqConfigService : IDnsmasqConfigService
|
||||
output.Add(DnsmasqConfDhcpHostLineParser.ToLine(entry));
|
||||
|
||||
var tmpPath = path + ".tmp";
|
||||
await File.WriteAllLinesAsync(tmpPath, output, Encoding.UTF8, ct);
|
||||
await File.WriteAllLinesAsync(tmpPath, output, DnsmasqFileEncoding.Utf8NoBom, ct);
|
||||
File.Move(tmpPath, path, overwrite: true);
|
||||
EnsureManagedHostsFileExists(set.ManagedHostsFilePath);
|
||||
var effectiveHostsPathDhcp = configLines.OfType<AddnHostsLine>().FirstOrDefault()?.AddnHostsPath ?? "";
|
||||
@@ -152,7 +156,7 @@ public class DnsmasqConfigService : IDnsmasqConfigService
|
||||
foreach (var file in set.Files.Where(f => !f.IsManaged && !string.Equals(f.Path, managedPath, StringComparison.Ordinal)))
|
||||
{
|
||||
if (!File.Exists(file.Path)) continue;
|
||||
var lines = await File.ReadAllLinesAsync(file.Path, Encoding.UTF8, ct);
|
||||
var lines = await File.ReadAllLinesAsync(file.Path, DnsmasqFileEncoding.Utf8NoBom, ct);
|
||||
var configLines = DnsmasqConfFileLineParser.ParseFile(lines);
|
||||
foreach (var dhcp in configLines.OfType<DhcpHostLine>().Select(c => c.DhcpHost))
|
||||
{
|
||||
@@ -191,11 +195,25 @@ public class DnsmasqConfigService : IDnsmasqConfigService
|
||||
var output = list.Select(DnsmasqConfFileLineParser.ToLine).ToList();
|
||||
|
||||
var tmpPath = path + ".tmp";
|
||||
await File.WriteAllLinesAsync(tmpPath, output, Encoding.UTF8, ct);
|
||||
await File.WriteAllLinesAsync(tmpPath, output, DnsmasqFileEncoding.Utf8NoBom, ct);
|
||||
File.Move(tmpPath, path, overwrite: true);
|
||||
EnsureManagedHostsFileExists(set.ManagedHostsFilePath);
|
||||
var effectiveHostsPath = list.OfType<AddnHostsLine>().FirstOrDefault()?.AddnHostsPath ?? "";
|
||||
_configSetCache.NotifyWeWroteManagedConfig(new ManagedConfigContent(list, effectiveHostsPath));
|
||||
_logger.LogInformation("Wrote managed config file: {Path}", path);
|
||||
}
|
||||
|
||||
/// <summary>Strip UTF-8 BOM from first line if present.</summary>
|
||||
private static IReadOnlyList<string> StripBomFromFirstLine(IReadOnlyList<string> lines)
|
||||
{
|
||||
if (lines.Count == 0) return lines;
|
||||
var first = lines[0];
|
||||
if (first.Length > 0 && first[0] == '\uFEFF')
|
||||
{
|
||||
var list = lines.ToList();
|
||||
list[0] = first[1..];
|
||||
return list;
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Text;
|
||||
using DnsmasqWebUI.Infrastructure;
|
||||
using DnsmasqWebUI.Models.Config;
|
||||
using DnsmasqWebUI.Models.EffectiveConfig;
|
||||
using DnsmasqWebUI.Configuration;
|
||||
@@ -48,8 +49,9 @@ public class EnsureManagedConfigHostedService : IApplicationHostedService
|
||||
var confFileLine = "conf-file=" + Path.GetRelativePath(mainDir, managedPath);
|
||||
|
||||
var lines = File.Exists(mainFull)
|
||||
? (await File.ReadAllLinesAsync(mainFull, Encoding.UTF8, cancellationToken)).ToList()
|
||||
? (await File.ReadAllLinesAsync(mainFull, DnsmasqFileEncoding.Utf8NoBom, cancellationToken)).ToList()
|
||||
: new List<string>();
|
||||
StripBomFromFirstLine(lines);
|
||||
|
||||
// Remove any conf-file= line that points to our managed path (we will add it as the last line).
|
||||
for (var i = lines.Count - 1; i >= 0; i--)
|
||||
@@ -75,12 +77,12 @@ public class EnsureManagedConfigHostedService : IApplicationHostedService
|
||||
if (lines.Count > 0 && !string.IsNullOrWhiteSpace(lines[^1]))
|
||||
lines.Add("");
|
||||
lines.Add(confFileLine);
|
||||
await File.WriteAllLinesAsync(mainFull, lines, Encoding.UTF8, cancellationToken);
|
||||
await File.WriteAllLinesAsync(mainFull, lines, DnsmasqFileEncoding.Utf8NoBom, cancellationToken);
|
||||
_logger.LogInformation("Set {Line} as the last line of main config {Path} so the managed file is included only by conf-file=.", confFileLine, mainFull);
|
||||
}
|
||||
else
|
||||
{
|
||||
await File.WriteAllLinesAsync(mainFull, lines, Encoding.UTF8, cancellationToken);
|
||||
await File.WriteAllLinesAsync(mainFull, lines, DnsmasqFileEncoding.Utf8NoBom, cancellationToken);
|
||||
}
|
||||
|
||||
Directory.CreateDirectory(managedDir);
|
||||
@@ -92,5 +94,14 @@ public class EnsureManagedConfigHostedService : IApplicationHostedService
|
||||
await configService.WriteManagedConfigAsync(Array.Empty<DnsmasqConfLine>(), cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>Strip UTF-8 BOM from first line if present, so we do not perpetuate it when writing.</summary>
|
||||
private static void StripBomFromFirstLine(List<string> lines)
|
||||
{
|
||||
if (lines.Count == 0) return;
|
||||
var first = lines[0];
|
||||
if (first.Length > 0 && first[0] == '\uFEFF')
|
||||
lines[0] = first[1..];
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Text;
|
||||
using DnsmasqWebUI.Infrastructure;
|
||||
using DnsmasqWebUI.Models.Hosts;
|
||||
using DnsmasqWebUI.Parsers;
|
||||
using DnsmasqWebUI.Services.Abstractions;
|
||||
@@ -36,7 +36,7 @@ public class HostsFileService : IHostsFileService
|
||||
|
||||
var tmpPath = path + ".tmp";
|
||||
var lines = entries.Select(HostsFileLineParser.ToLine).ToList();
|
||||
await File.WriteAllLinesAsync(tmpPath, lines, Encoding.UTF8, ct);
|
||||
await File.WriteAllLinesAsync(tmpPath, lines, DnsmasqFileEncoding.Utf8NoBom, ct);
|
||||
File.Move(tmpPath, path, overwrite: true);
|
||||
_hostsCache.NotifyWeWroteManagedHosts(entries);
|
||||
_logger.LogInformation("Wrote managed hosts file: {Path}", path);
|
||||
|
||||
Reference in New Issue
Block a user