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.Reflection.PortableExecutable;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ZuneModCore.Services;
|
||||
using ZuneModCore.Win32;
|
||||
using static ZuneModCore.Mods.FeaturesOverrideMod;
|
||||
|
||||
namespace ZuneModCore.Mods;
|
||||
@@ -146,6 +146,18 @@ public partial class WebservicesMod(ModMetadata metadata, IModCoreConfig modConf
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -303,57 +315,36 @@ public partial class WebservicesMod(ModMetadata metadata, IModCoreConfig modConf
|
||||
return null;
|
||||
}
|
||||
|
||||
private static async Task<string?> AddEntriesToHostsFile(string newHost)
|
||||
private async Task<string?> AddEntriesToHostsFile(string newHost)
|
||||
{
|
||||
var hostEntry = await Dns.GetHostEntryAsync(newHost);
|
||||
var ipAddress = hostEntry?.AddressList?.FirstOrDefault();
|
||||
if (ipAddress is null)
|
||||
List<HostsEntry> newEntries = new(_knownSubdomains.Length);
|
||||
|
||||
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");
|
||||
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);
|
||||
await HostsEditor.AddOrUpdateHostnamesWithIPAsync(newEntries);
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -441,8 +432,5 @@ public partial class WebservicesMod(ModMetadata metadata, IModCoreConfig modConf
|
||||
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 }
|
||||
}
|
||||
|
||||
@@ -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