mirror of
https://github.com/ZuneDev/ZuneModdingHelper.git
synced 2026-07-27 13:12:21 -07:00
Create hosts editor and add metaservices
This commit is contained in:
@@ -10,10 +10,10 @@ using System.Net;
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Reflection.PortableExecutable;
|
using System.Reflection.PortableExecutable;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using ZuneModCore.Services;
|
using ZuneModCore.Services;
|
||||||
|
using ZuneModCore.Win32;
|
||||||
using static ZuneModCore.Mods.FeaturesOverrideMod;
|
using static ZuneModCore.Mods.FeaturesOverrideMod;
|
||||||
|
|
||||||
namespace ZuneModCore.Mods;
|
namespace ZuneModCore.Mods;
|
||||||
@@ -146,6 +146,18 @@ public partial class WebservicesMod(ModMetadata metadata, IModCoreConfig modConf
|
|||||||
await RecordAppliedMethod(ApplicationMethod.BinaryPatch);
|
await RecordAppliedMethod(ApplicationMethod.BinaryPatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Also set up the metaservices hosts
|
||||||
|
var ipAddress = IPAddress.Parse("135.181.88.32");
|
||||||
|
IEnumerable<string> hostnames = [
|
||||||
|
"toc.music.metaservices.microsoft.com",
|
||||||
|
"info.music.metaservices.microsoft.com",
|
||||||
|
"images.metaservices.microsoft.com",
|
||||||
|
"redir.metaservices.microsoft.com",
|
||||||
|
"windowsmedia.com",
|
||||||
|
];
|
||||||
|
var newEntries = hostnames.Select(h => new HostsEntry(ipAddress, h));
|
||||||
|
await HostsEditor.AddOrUpdateHostnamesWithIPAsync(newEntries);
|
||||||
|
|
||||||
return errorMessage;
|
return errorMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,57 +315,36 @@ public partial class WebservicesMod(ModMetadata metadata, IModCoreConfig modConf
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<string?> AddEntriesToHostsFile(string newHost)
|
private async Task<string?> AddEntriesToHostsFile(string newHost)
|
||||||
{
|
{
|
||||||
var hostEntry = await Dns.GetHostEntryAsync(newHost);
|
List<HostsEntry> newEntries = new(_knownSubdomains.Length);
|
||||||
var ipAddress = hostEntry?.AddressList?.FirstOrDefault();
|
|
||||||
if (ipAddress is null)
|
foreach (var subdomain in _knownSubdomains.Select(e => e.subdomain))
|
||||||
{
|
{
|
||||||
return $"Failed to resolve host '{newHost}' to an IP address.";
|
var newHostname = GetDomainName(subdomain, newHost);
|
||||||
|
|
||||||
|
IPAddress? ipAddress;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var dnsEntry = await Dns.GetHostEntryAsync(newHostname);
|
||||||
|
ipAddress = dnsEntry?.AddressList?.FirstOrDefault();
|
||||||
|
if (ipAddress is null)
|
||||||
|
{
|
||||||
|
log.LogError("Failed to resolve host '{hostname}' to an IP address.", newHostname);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
log.Log(LogLevel.Error, ex, "Failed to resolve host '{hostname}' to an IP address.", newHostname);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var oldHostname = GetDomainName(subdomain, "zune.net");
|
||||||
|
newEntries.Add(new(ipAddress, oldHostname));
|
||||||
}
|
}
|
||||||
|
|
||||||
var hostsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), @"drivers\etc\hosts");
|
await HostsEditor.AddOrUpdateHostnamesWithIPAsync(newEntries);
|
||||||
var hostLines = (await File.ReadAllLinesAsync(hostsPath)).ToList();
|
|
||||||
|
|
||||||
var domains = _knownSubdomains
|
|
||||||
.Select(e => GetDomainName(e.subdomain, "zune.net"))
|
|
||||||
.ToHashSet();
|
|
||||||
|
|
||||||
// Update existing entries
|
|
||||||
Regex rx = HostsEntryRegex();
|
|
||||||
for (int l = 0; l < hostLines.Count; l++)
|
|
||||||
{
|
|
||||||
var line = hostLines[l].Trim();
|
|
||||||
|
|
||||||
// Ignore comments and empty lines
|
|
||||||
if (line.Length <= 0 || line[0] is '#')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var match = rx.Match(line);
|
|
||||||
if (!match.Success)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var domain = match.Groups["name"].Value;
|
|
||||||
|
|
||||||
// Ignore entries unrelated to Zune
|
|
||||||
if (!domain.EndsWith("zune.net"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Reconstruct entry with new IP address
|
|
||||||
hostLines[l] = $"{ipAddress} {domain}";
|
|
||||||
|
|
||||||
// Track that we handled this domain
|
|
||||||
domains.Remove(domain);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add any entries that didn't already exist
|
|
||||||
if (domains.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var domain in domains)
|
|
||||||
hostLines.Add($"{ipAddress} {domain}");
|
|
||||||
}
|
|
||||||
|
|
||||||
await File.WriteAllLinesAsync(hostsPath, hostLines);
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -441,8 +432,5 @@ public partial class WebservicesMod(ModMetadata metadata, IModCoreConfig modConf
|
|||||||
return Enum.Parse<ApplicationMethod>(methodText);
|
return Enum.Parse<ApplicationMethod>(methodText);
|
||||||
}
|
}
|
||||||
|
|
||||||
[GeneratedRegex(@"(?<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(?<name>[\w.-]+?)(\s+|$)")]
|
|
||||||
private static partial Regex HostsEntryRegex();
|
|
||||||
|
|
||||||
private enum ApplicationMethod { None, BinaryPatch, HostsEntries }
|
private enum ApplicationMethod { None, BinaryPatch, HostsEntries }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace ZuneModCore.Win32;
|
||||||
|
|
||||||
|
public static partial class HostsEditor
|
||||||
|
{
|
||||||
|
private static readonly string _hostsPath = Environment.ExpandEnvironmentVariables(@"%SystemRoot%\System32\drivers\etc\hosts");
|
||||||
|
|
||||||
|
[GeneratedRegex(@"(?<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(?<name>[\w.-]+?)(\s+|$)")]
|
||||||
|
private static partial Regex HostsEntryRegex();
|
||||||
|
|
||||||
|
public static async Task AddOrUpdateHostnamesWithIPAsync(IEnumerable<HostsEntry> newEntries)
|
||||||
|
{
|
||||||
|
var unhandledEntries = newEntries
|
||||||
|
.ToDictionary(e => e.Hostname, e => e.IPAddress);
|
||||||
|
|
||||||
|
var hostLines = await ReadHostsFileAsync();
|
||||||
|
|
||||||
|
// Update existing entries
|
||||||
|
Regex rx = HostsEntryRegex();
|
||||||
|
for (int l = 0; l < hostLines.Count; l++)
|
||||||
|
{
|
||||||
|
var line = hostLines[l].Trim();
|
||||||
|
|
||||||
|
// Ignore comments and empty lines
|
||||||
|
if (line.Length <= 0 || line[0] is '#')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var match = rx.Match(line);
|
||||||
|
if (!match.Success)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var hostname = match.Groups["name"].Value;
|
||||||
|
var ip = match.Groups["ip"].Value;
|
||||||
|
|
||||||
|
// Ignore entries we've already handled
|
||||||
|
if (!unhandledEntries.TryGetValue(hostname, out var newIP))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Avoid changing lines when we don't have to. This
|
||||||
|
// preserves trivia such as whitespace and comments when possible
|
||||||
|
if (IPAddress.Parse(ip) == newIP)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Reconstruct entry with new IP address
|
||||||
|
hostLines[l] = $"{newIP} {hostname}";
|
||||||
|
|
||||||
|
// Track that we handled this domain
|
||||||
|
unhandledEntries.Remove(hostname);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add any entries that didn't already exist
|
||||||
|
if (unhandledEntries.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var (hostname, ip) in unhandledEntries)
|
||||||
|
hostLines.Add($"{ip} {hostname}");
|
||||||
|
}
|
||||||
|
|
||||||
|
await WriteHostsFileAsync(hostLines);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<List<string>> ReadHostsFileAsync()
|
||||||
|
{
|
||||||
|
var lines = await File.ReadAllLinesAsync(_hostsPath);
|
||||||
|
return lines.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task WriteHostsFileAsync(List<string> lines)
|
||||||
|
{
|
||||||
|
await File.WriteAllLinesAsync(_hostsPath, lines);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HostsEntry(IPAddress ipAddress, string hostname, string? originalString = null)
|
||||||
|
: IEquatable<HostsEntry>
|
||||||
|
{
|
||||||
|
private IPAddress _ipAddress = ipAddress;
|
||||||
|
private string _hostname = hostname;
|
||||||
|
private string? _originalString = originalString;
|
||||||
|
|
||||||
|
public IPAddress IPAddress
|
||||||
|
{
|
||||||
|
get => _ipAddress;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_originalString = null;
|
||||||
|
_ipAddress = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Hostname
|
||||||
|
{
|
||||||
|
get => _hostname;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_originalString = null;
|
||||||
|
_hostname = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(HostsEntry? other)
|
||||||
|
{
|
||||||
|
if (other is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return IPAddress.Equals(other.IPAddress)
|
||||||
|
&& Hostname.Equals(other.Hostname, StringComparison.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object obj) => Equals(obj as HostsEntry);
|
||||||
|
|
||||||
|
public override string ToString() => _originalString ?? $"{IPAddress} {Hostname}";
|
||||||
|
|
||||||
|
public override int GetHashCode() => (IPAddress, Hostname).GetHashCode();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user