Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of PortalPass v0.1.2
PortalPass.dll
Decompiled 3 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Net; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("PortalPass")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Client-side Valheim password injection with endpoint-aware matching")] [assembly: AssemblyFileVersion("0.1.2.0")] [assembly: AssemblyInformationalVersion("0.1.2+d55f562a6d3c4b925f9056f3fb79acc0862411a1")] [assembly: AssemblyProduct("PortalPass")] [assembly: AssemblyTitle("PortalPass")] [assembly: AssemblyVersion("0.1.2.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Jcruse03.PortalPass { internal static class ConnectionEndpoint { private static string? _pendingHost; private static int _pendingPort; internal static void Capture(string? host, int port) { if (string.IsNullOrWhiteSpace(host) || port < 1 || port > 65535) { Clear(); return; } _pendingHost = host; _pendingPort = port; } internal static void Clear() { _pendingHost = null; _pendingPort = 0; } internal static bool TryConsume(string? liveHost, int livePort, out string host, out int port) { if (!string.IsNullOrWhiteSpace(_pendingHost)) { int pendingPort = _pendingPort; if (pendingPort >= 1 && pendingPort <= 65535) { host = _pendingHost; port = _pendingPort; Clear(); return true; } } Clear(); if (!string.IsNullOrWhiteSpace(liveHost) && livePort >= 1 && livePort <= 65535) { host = liveHost; port = livePort; return true; } host = string.Empty; port = 0; return false; } } internal readonly struct EndpointKey : IEquatable<EndpointKey> { internal string Host { get; } internal int? Port { get; } internal EndpointKey(string host, int? port) { Host = host; Port = port; } internal static EndpointKey Parse(string text) { if (string.IsNullOrWhiteSpace(text)) { throw new FormatException("Endpoint is empty."); } string text2 = text.Trim(); int? port = null; string value; if (text2[0] == '[') { int num = text2.IndexOf(']'); if (num < 0) { throw new FormatException("Bracketed IPv6 endpoint is missing ']'."); } value = text2.Substring(1, num - 1); string text3 = text2.Substring(num + 1); if (text3.Length > 0) { if (text3[0] != ':') { throw new FormatException("Unexpected text after bracketed host."); } port = ParsePort(text3.Substring(1)); } } else { int num2 = text2.IndexOf(':'); int num3 = text2.LastIndexOf(':'); if (num2 >= 0 && num2 == num3) { value = text2.Substring(0, num2); port = ParsePort(text2.Substring(num2 + 1)); } else { value = text2; } } return new EndpointKey(NormalizeHost(value), port); } internal static EndpointKey FromConnection(string host, int port) { if (port < 1 || port > 65535) { throw new FormatException("Connection port is outside 1-65535."); } return new EndpointKey(NormalizeHost(host), port); } internal EndpointKey WithoutPort() { return new EndpointKey(Host, null); } private static int ParsePort(string value) { if (!int.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out var result) || result < 1 || result > 65535) { throw new FormatException("Port must be an integer from 1 through 65535."); } return result; } private static string NormalizeHost(string value) { string text = value.Trim(); if (text.Length == 0) { throw new FormatException("Host is empty."); } if (text.IndexOfAny(new char[9] { '/', '\\', '@', '#', '=', ' ', '\t', '\r', '\n' }) >= 0) { throw new FormatException("Host contains unsupported characters."); } if (IPAddress.TryParse(text, out IPAddress address)) { return address.ToString().ToLowerInvariant(); } text = text.TrimEnd(new char[1] { '.' }).ToLowerInvariant(); if (text.Length == 0) { throw new FormatException("Host is empty after normalization."); } return text; } public bool Equals(EndpointKey other) { if (Port == other.Port) { return string.Equals(Host, other.Host, StringComparison.Ordinal); } return false; } public override bool Equals(object? obj) { if (obj is EndpointKey other) { return Equals(other); } return false; } public override int GetHashCode() { return (Host.GetHashCode() * 397) ^ Port.GetValueOrDefault(); } public override string ToString() { if (!Port.HasValue) { return Host; } if (!Host.Contains(":")) { return $"{Host}:{Port.Value}"; } return $"[{Host}]:{Port.Value}"; } } [HarmonyPatch(typeof(FejdStartup), "SetServerToJoin")] internal static class ServerSelectionPatch { private static void Prefix(ServerJoinData serverData) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) if ((int)serverData.m_type == 3) { ServerJoinDataDedicated dedicated = ((ServerJoinData)(ref serverData)).Dedicated; ConnectionEndpoint.Capture(((ServerJoinDataDedicated)(ref dedicated)).GetHost(), dedicated.m_port); } else { ConnectionEndpoint.Clear(); } } } [HarmonyPatch(typeof(ZNet), "RPC_ClientHandshake")] internal static class PasswordHandshakePatch { private static readonly FieldInfo ServerHostField = AccessTools.Field(typeof(ZNet), "m_serverHost"); private static readonly FieldInfo ServerPortField = AccessTools.Field(typeof(ZNet), "m_serverHostPort"); private static readonly FieldInfo ServerPasswordField = AccessTools.Field(typeof(FejdStartup), "<ServerPassword>k__BackingField"); private static string? _injectedPassword; private static void Prefix(bool needPassword) { ClearOwnedPassword(); if (!needPassword) { ConnectionEndpoint.Clear(); return; } string liveHost = ServerHostField.GetValue(null) as string; int livePort = ((ServerPortField.GetValue(null) is int num) ? num : 0); if (!ConnectionEndpoint.TryConsume(liveHost, livePort, out string host, out int port)) { PortalPassPlugin.Log.LogWarning((object)"Could not determine the requested dedicated endpoint after backend resolution. Leaving the vanilla password prompt active."); return; } if (!SecretFile.TryResolve(PortalPassPlugin.SecretFilePath.Value, host, port, PortalPassPlugin.Log, out string password)) { PortalPassPlugin.Log.LogInfo((object)("No configured password matched " + DisplayEndpoint(host, port) + ". Leaving the vanilla password prompt active.")); return; } _injectedPassword = password; ServerPasswordField.SetValue(null, password); PortalPassPlugin.Log.LogInfo((object)("Supplying a configured password for " + DisplayEndpoint(host, port) + ".")); } private static void Postfix() { ClearOwnedPassword(); } private static void ClearOwnedPassword() { if (_injectedPassword != null && string.Equals(FejdStartup.ServerPassword, _injectedPassword, StringComparison.Ordinal)) { ServerPasswordField.SetValue(null, null); } _injectedPassword = null; } private static string DisplayEndpoint(string host, int port) { if (!host.Contains(":")) { return $"{host}:{port}"; } return $"[{host}]:{port}"; } } internal sealed class PasswordMap { private readonly Dictionary<EndpointKey, string> _passwords; private PasswordMap(Dictionary<EndpointKey, string> passwords) { _passwords = passwords; } internal static PasswordMap Parse(IEnumerable<string> lines) { Dictionary<EndpointKey, string> dictionary = new Dictionary<EndpointKey, string>(); int num = 0; foreach (string line in lines) { num++; string text = line.Trim(); if (text.Length != 0 && !text.StartsWith("#", StringComparison.Ordinal)) { int num2 = text.IndexOf('='); if (num2 < 1) { throw new FormatException($"Line {num}: expected endpoint=password."); } EndpointKey endpointKey; try { endpointKey = EndpointKey.Parse(text.Substring(0, num2)); } catch (FormatException ex) { throw new FormatException($"Line {num}: {ex.Message}", ex); } string text2 = Unquote(text.Substring(num2 + 1).Trim()); if (text2.Length == 0) { throw new FormatException($"Line {num}: password is empty."); } if (dictionary.ContainsKey(endpointKey)) { throw new FormatException($"Line {num}: duplicate endpoint after normalization: {endpointKey}."); } dictionary.Add(endpointKey, text2); } } return new PasswordMap(dictionary); } internal static PasswordMap Load(string path) { return Parse(File.ReadLines(path)); } internal bool TryResolve(string host, int port, out string password) { EndpointKey key = EndpointKey.FromConnection(host, port); if (!_passwords.TryGetValue(key, out password)) { return _passwords.TryGetValue(key.WithoutPort(), out password); } return true; } private static string Unquote(string value) { if (value.Length < 2) { return value; } char c = value[0]; char c2 = value[value.Length - 1]; if ((c == '\'' && c2 == '\'') || (c == '"' && c2 == '"')) { return value.Substring(1, value.Length - 2); } if (c == '\'' || c == '"' || c2 == '\'' || c2 == '"') { throw new FormatException("Password has an unmatched quote."); } return value; } } [BepInPlugin("com.jcruse03.portalpass", "PortalPass", "0.1.2")] public sealed class PortalPassPlugin : BaseUnityPlugin { internal const string PluginGuid = "com.jcruse03.portalpass"; internal const string PluginName = "PortalPass"; internal const string PluginVersion = "0.1.2"; internal static ConfigEntry<string> SecretFilePath; internal static ManualLogSource Log; private Harmony? _harmony; private void Awake() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; SecretFilePath = ((BaseUnityPlugin)this).Config.Bind<string>("General", "SecretFilePath", SecretFile.DefaultPath(), "Absolute path (or ~/ path) to the endpoint=password secret file. Keep this outside r2modman profiles."); _harmony = new Harmony("com.jcruse03.portalpass"); _harmony.PatchAll(Assembly.GetExecutingAssembly()); ((BaseUnityPlugin)this).Logger.LogInfo((object)("PortalPass 0.1.2 loaded (client-only). Secrets: " + SecretFile.ExpandPath(SecretFilePath.Value))); } private void OnDestroy() { Harmony? harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } internal static class SecretFile { internal static string DefaultPath() { if (Environment.OSVersion.Platform == PlatformID.Win32NT) { return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "PortalPass", "passwords.env"); } string text = Environment.GetEnvironmentVariable("XDG_CONFIG_HOME"); if (string.IsNullOrWhiteSpace(text)) { text = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config"); } return Path.Combine(text, "portalpass", "passwords.env"); } internal static string ExpandPath(string path) { string text = Environment.ExpandEnvironmentVariables(path.Trim()); if (text == "~") { return Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); } if (text.StartsWith("~/", StringComparison.Ordinal) || text.StartsWith("~\\", StringComparison.Ordinal)) { return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), text.Substring(2)); } return Path.GetFullPath(text); } internal static bool TryResolve(string path, string host, int port, ManualLogSource logger, out string password) { password = string.Empty; try { string text = ExpandPath(path); if (!File.Exists(text)) { logger.LogWarning((object)("Secret file not found: " + text + ". Leaving the vanilla password prompt active.")); return false; } return PasswordMap.Load(text).TryResolve(host, port, out password); } catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException || ex is FormatException || ex is ArgumentException) { logger.LogWarning((object)("Could not load PortalPass secrets (" + ex.Message + "). Leaving the vanilla password prompt active.")); return false; } } } }