Skip to content

Commit

Permalink
Early support for IPv6 Host/Join
Browse files Browse the repository at this point in the history
  • Loading branch information
TIRTAGT committed Feb 12, 2024
1 parent 5a345f5 commit e7c31a7
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 9 deletions.
1 change: 1 addition & 0 deletions LCDirectLan.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ private void Awake()
config.Bind<byte>("Join", "HideRawJoinData", 3, new ConfigDescription("Do not display or save the recently joined server data to avoid Server Leak, useful for streamers.\nThis config accepts a bitwise value\nExamples:\n0: Show anything (Disabled/No Hiding)\n1: Hide IP Address\n2. Hide Port Number\n3. Hide IP and Port\n4. Hide Hostname\n7. Hide all of them (IP,Port,Hostname)", new AcceptableValueList<byte>(new byte[] { 0, 1, 2, 3, 4, 7 })));

/* Default network configuration when hosting LAN lobbies */
config.Bind<bool>("Host", "ListenOnIPv6", false, new ConfigDescription("Should the game listen on IPv6 when hosting instead of IPv4 ?\nDual-Stack Listening does not seem to be supported by default, custom implementation coming soon ?"));
config.Bind<ushort>("Host", "DefaultPort", 7777, new ConfigDescription("Default Port for hosting, the default vanilla port is 7777"));

/* Custom Username Feature / Patches */
Expand Down
38 changes: 32 additions & 6 deletions Patches/ConfigurableLAN/MenuManagerPatch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ public static void PrepareDirectLANDialog(MenuManager __instance, ref Boolean __
if (HideJoinData > 0) {
LCDirectLan.Log(BepInEx.Logging.LogLevel.Info, $"Applying server leak protection based on HideRawJoinData's bitwise value: {HideJoinData}");

// Check if we should hide IP Address
if ((HideJoinData & 1) == 1 && !string.IsNullOrEmpty(PublicServerJoinData.Address) && ResolveDNS.IsValidIPv4(PublicServerJoinData.Address)) {
// Check if we should hide IP Address
if ((HideJoinData & 1) == 1 && !string.IsNullOrEmpty(PublicServerJoinData.Address) && ResolveDNS.CheckIPType(PublicServerJoinData.Address) != System.Net.Sockets.AddressFamily.Unknown) {
PublicServerJoinData.Address = "";
LCDirectLan.Log(BepInEx.Logging.LogLevel.Info, "Server IP Address from config is cleared !");
}
Expand Down Expand Up @@ -373,7 +373,7 @@ private static void CreateDirectConnectWindow()
GameObject ServerNameField_GameObject = DCSettingsContainer.transform.Find("ServerNameField").gameObject;
ServerNameField_GameObject.transform.SetLocalPositionAndRotation(new Vector3(0, 110, 0), ServerNameField_GameObject.transform.localRotation);
TMP_InputField ServerNameInputField = ServerNameField_GameObject.GetComponent<TMP_InputField>();
((TextMeshProUGUI)ServerNameInputField.placeholder).text = "127.0.0.1";
((TextMeshProUGUI)ServerNameInputField.placeholder).text = "";
ServerNameInputField.text = "";
ServerNameInputField.onValueChanged.AddListener(new UnityEngine.Events.UnityAction<string>(OnServerNameInputField_Changed));

Expand Down Expand Up @@ -621,7 +621,7 @@ private static void OnDirectJoinConnect()
}

// Check if the input looks like a valid hostname
if (!ResolveDNS.IsValidIPv4(PrivateServerJoinData.Address) && ResolveDNS.IsOnHostnameFormat(PrivateServerJoinData.Address)) {
if (ResolveDNS.CheckIPType(PrivateServerJoinData.Address) == System.Net.Sockets.AddressFamily.Unknown && ResolveDNS.IsOnHostnameFormat(PrivateServerJoinData.Address)) {
LCDirectLan.Log(BepInEx.Logging.LogLevel.Info, "Detected a valid hostname, trying to resolve it...");

__MenuManager.StartCoroutine(PerformDNSAutoConfigure(PrivateServerJoinData.Address, false, ResolvedData => {
Expand Down Expand Up @@ -668,8 +668,8 @@ private static void OnDirectJoinConnect()
/// Continue the direct join connect process (this is used to make ASYNC DNS resolve compatible with non-DNS connect)
/// </summary>
private static void ContinueDirectJoinConnect() {
// Check if this is not a valid IPv4 (also checks if the DNS resolved IP is valid too)
if (!ResolveDNS.IsValidIPv4(PrivateServerJoinData.Address))
// Check if the input field or DNS resolved IP is not valid
if (ResolveDNS.CheckIPType(PrivateServerJoinData.Address) == System.Net.Sockets.AddressFamily.Unknown)
{
LCDirectLan.Log(BepInEx.Logging.LogLevel.Error, "Invalid Server IP/Hostname (final check)");
__MenuManager.SetLoadingScreen(false, Steamworks.RoomEnter.Error, "Invalid Server IP/Hostname");
Expand Down Expand Up @@ -908,6 +908,32 @@ public static void Prefix_ClickHostButton()
GameObject.Find("NetworkManager").GetComponent<UnityTransport>().ConnectionData.Port = LCDirectLan.GetConfig<ushort>("Host", "DefaultPort");
}

[HarmonyPatch("StartHosting")]
[HarmonyPrefix]
[HarmonyPriority(Priority.VeryLow)]
public static void Prefix_StartHosting()
{
// Do not do anything when not in LAN mode
if (!LCDirectLan.IsOnLanMode) { return; }

// Check if we should not listen on IPv6 instead of IPv4 which is the default
if (!LCDirectLan.GetConfig<bool>("Host", "ListenOnIPv6")) {
return;
}

UnityTransport a = GameObject.Find("NetworkManager").GetComponent<UnityTransport>();

// Check if we should listen on localhost or any
if (a.ConnectionData.ServerListenAddress == "127.0.0.1") {
GameObject.Find("NetworkManager").GetComponent<UnityTransport>().ConnectionData.ServerListenAddress = "::1";
LCDirectLan.Log(BepInEx.Logging.LogLevel.Debug, "Server Listen Address changed to IPv6 Localhost/Loopback (::1)");
return;
}

GameObject.Find("NetworkManager").GetComponent<UnityTransport>().ConnectionData.ServerListenAddress = "::";
LCDirectLan.Log(BepInEx.Logging.LogLevel.Debug, "Server Listen Address changed to IPv6 Any Address (::1)");
}

/// <summary>
/// Utility function to change the game's LoadingText value while direct join is on process
/// </summary>
Expand Down
36 changes: 33 additions & 3 deletions Utility/ResolveDNS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

using System;
using System.Net;
using System.Net.Sockets;
using System.Text.RegularExpressions;
using DnsClient;
using DnsClient.Protocol;
Expand Down Expand Up @@ -111,20 +112,49 @@ public static (string, UInt16) ResolveSRVRecord(string record_name)
}

/// <summary>
/// Check if a string is a valid IPv4 Address using RegEx
/// Check if a string is a valid IPv4 Address
/// </summary>
/// <param name="ip">The string to be tested</param>
/// <param name="ip">The string to be checked</param>
/// <returns>Boolean representing whether the string is a valid IPv4</returns>
public static bool IsValidIPv4(string ip)
{
if (IPAddress.TryParse(ip, out IPAddress a))
{
return a.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork;
return a.AddressFamily == AddressFamily.InterNetwork;
}

return false;
}

/// <summary>
/// Check if a string is a valid IPv6 Address
/// </summary>
/// <param name="ip">The string to be checked</param>
/// <returns>Boolean representing whether the string is a valid IPv6</returns>
public static bool IsValidIPv6(string ip)
{
if (IPAddress.TryParse(ip, out IPAddress a))
{
return a.AddressFamily == AddressFamily.InterNetworkV6;
}

return false;
}

/// <summary>
/// Check if a string is a valid IPv4/IPv6 Address
/// </summary>
/// <param name="ip">The string to be checked</param>
/// <returns>An AddressFamily enum with:<br></br> - InterNetwork for IPv4<br></br> - InterNetworkV6 for IPv6<br></br> - Unknown otherwise.</returns>
public static AddressFamily CheckIPType(string ip)
{
if (IsValidIPv4(ip)) { return AddressFamily.InterNetwork; }

if (IsValidIPv6(ip)) { return AddressFamily.InterNetworkV6; }

return AddressFamily.Unknown;
}

public static bool IsOnHostnameFormat(string hostname)
{
// If it's localhost, return true
Expand Down

0 comments on commit e7c31a7

Please sign in to comment.