From 00684bea5d5e33a592802373074ebf809c0b03f9 Mon Sep 17 00:00:00 2001 From: Simon Lemay Date: Tue, 5 Aug 2025 17:03:26 -0400 Subject: [PATCH 1/8] Get rid of unnecessary UTP_TRANSPORT_2_4_ABOVE define --- .../Runtime/Transports/UTP/UnityTransport.cs | 10 +++++----- .../Runtime/Unity.Netcode.Runtime.asmdef | 5 ----- .../Tests/Editor/Transports/UnityTransportTests.cs | 4 ++-- .../Tests/Editor/Unity.Netcode.Editor.Tests.asmdef | 5 ----- .../Transports/UnityTransportConnectionTests.cs | 2 +- .../Tests/Runtime/Unity.Netcode.Runtime.Tests.asmdef | 5 ----- 6 files changed, 8 insertions(+), 23 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs index 84513fde66..3f8fbd7171 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs @@ -7,7 +7,7 @@ using System; using System.Collections.Generic; -#if HOSTNAME_RESOLUTION_AVAILABLE && UTP_TRANSPORT_2_4_ABOVE +#if HOSTNAME_RESOLUTION_AVAILABLE using System.Text.RegularExpressions; #endif using Unity.Burst; @@ -260,7 +260,7 @@ public NetworkEndpoint ServerEndPoint var networkEndpoint = ParseNetworkEndpoint(Address, Port); if (networkEndpoint == default) { -#if HOSTNAME_RESOLUTION_AVAILABLE && UTP_TRANSPORT_2_4_ABOVE +#if HOSTNAME_RESOLUTION_AVAILABLE if (!IsValidFqdn(Address)) #endif { @@ -667,7 +667,7 @@ private NetworkPipeline SelectSendPipeline(NetworkDelivery delivery) } } -#if HOSTNAME_RESOLUTION_AVAILABLE && UTP_TRANSPORT_2_4_ABOVE +#if HOSTNAME_RESOLUTION_AVAILABLE private static bool IsValidFqdn(string fqdn) { // Regular expression to validate FQDN @@ -693,7 +693,7 @@ private bool ClientBindAndConnect() // Verify the endpoint is valid before proceeding if (serverEndpoint.Family == NetworkFamily.Invalid) { -#if HOSTNAME_RESOLUTION_AVAILABLE && UTP_TRANSPORT_2_4_ABOVE +#if HOSTNAME_RESOLUTION_AVAILABLE // If it's not valid, assure it meets FQDN standards if (IsValidFqdn(ConnectionData.Address)) @@ -745,7 +745,7 @@ private bool ServerBindAndListen(NetworkEndpoint endPoint) // Verify the endpoint is valid before proceeding if (endPoint.Family == NetworkFamily.Invalid) { -#if HOSTNAME_RESOLUTION_AVAILABLE && UTP_TRANSPORT_2_4_ABOVE +#if HOSTNAME_RESOLUTION_AVAILABLE // If it's not valid, assure it meets FQDN standards if (!IsValidFqdn(ConnectionData.Address)) { diff --git a/com.unity.netcode.gameobjects/Runtime/Unity.Netcode.Runtime.asmdef b/com.unity.netcode.gameobjects/Runtime/Unity.Netcode.Runtime.asmdef index e4de012706..d0a5743b42 100644 --- a/com.unity.netcode.gameobjects/Runtime/Unity.Netcode.Runtime.asmdef +++ b/com.unity.netcode.gameobjects/Runtime/Unity.Netcode.Runtime.asmdef @@ -68,11 +68,6 @@ "expression": "6000.0.11f1", "define": "COM_UNITY_MODULES_PHYSICS2D_LINEAR" }, - { - "name": "com.unity.transport", - "expression": "2.4.0", - "define": "UTP_TRANSPORT_2_4_ABOVE" - }, { "name": "Unity", "expression": "6000.1.0a1", diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs index 6762fc954d..a7ae940618 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs @@ -132,7 +132,7 @@ public void UnityTransport_RestartSucceedsAfterFailure() Assert.False(transport.StartServer()); LogAssert.Expect(LogType.Error, "Invalid network endpoint: 127.0.0.:4242."); -#if HOSTNAME_RESOLUTION_AVAILABLE && UTP_TRANSPORT_2_4_ABOVE +#if HOSTNAME_RESOLUTION_AVAILABLE LogAssert.Expect(LogType.Error, "Listen network address (127.0.0.) is not a valid Ipv4 or Ipv6 address!"); #else LogAssert.Expect(LogType.Error, "Network listen address (127.0.0.) is Invalid!"); @@ -166,7 +166,7 @@ public void UnityTransport_StartClientFailsWithBadAddress() transport.SetConnectionData("foobar", 4242); Assert.False(transport.StartClient()); LogAssert.Expect(LogType.Error, "Invalid network endpoint: foobar:4242."); -#if HOSTNAME_RESOLUTION_AVAILABLE && UTP_TRANSPORT_2_4_ABOVE +#if HOSTNAME_RESOLUTION_AVAILABLE LogAssert.Expect(LogType.Error, "Target server network address (foobar) is not a valid Fully Qualified Domain Name!"); #else LogAssert.Expect(LogType.Error, "Target server network address (foobar) is Invalid!"); diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Unity.Netcode.Editor.Tests.asmdef b/com.unity.netcode.gameobjects/Tests/Editor/Unity.Netcode.Editor.Tests.asmdef index 04ed44cf77..976fa7be17 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Unity.Netcode.Editor.Tests.asmdef +++ b/com.unity.netcode.gameobjects/Tests/Editor/Unity.Netcode.Editor.Tests.asmdef @@ -38,11 +38,6 @@ "expression": "(0,2022.2.0a5)", "define": "UNITY_UNET_PRESENT" }, - { - "name": "com.unity.transport", - "expression": "2.4.0", - "define": "UTP_TRANSPORT_2_4_ABOVE" - }, { "name": "Unity", "expression": "6000.1.0a1", diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportConnectionTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportConnectionTests.cs index 2771ae6299..03674f9977 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportConnectionTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportConnectionTests.cs @@ -67,7 +67,7 @@ public void DetectInvalidEndpoint() m_Clients[0].ConnectionData.Address = "MoreFubar"; Assert.False(m_Server.StartServer(), "Server failed to detect invalid endpoint!"); Assert.False(m_Clients[0].StartClient(), "Client failed to detect invalid endpoint!"); -#if HOSTNAME_RESOLUTION_AVAILABLE && UTP_TRANSPORT_2_4_ABOVE +#if HOSTNAME_RESOLUTION_AVAILABLE LogAssert.Expect(LogType.Error, $"Listen network address ({m_Server.ConnectionData.Address}) is not a valid {Networking.Transport.NetworkFamily.Ipv4} or {Networking.Transport.NetworkFamily.Ipv6} address!"); LogAssert.Expect(LogType.Error, $"Target server network address ({m_Clients[0].ConnectionData.Address}) is not a valid Fully Qualified Domain Name!"); diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Unity.Netcode.Runtime.Tests.asmdef b/com.unity.netcode.gameobjects/Tests/Runtime/Unity.Netcode.Runtime.Tests.asmdef index 8a7507f158..d2b3924e2a 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Unity.Netcode.Runtime.Tests.asmdef +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Unity.Netcode.Runtime.Tests.asmdef @@ -48,11 +48,6 @@ "expression": "", "define": "COM_UNITY_MODULES_PHYSICS" }, - { - "name": "com.unity.transport", - "expression": "2.4.0", - "define": "UTP_TRANSPORT_2_4_ABOVE" - }, { "name": "Unity", "expression": "6000.1.0a1", From 2551c158e10c6759b55d97dae189875b0c491971 Mon Sep 17 00:00:00 2001 From: Simon Lemay Date: Wed, 6 Aug 2025 13:13:16 -0400 Subject: [PATCH 2/8] Make ConnectionAddressData.ServerEndPoint obsolete --- .../Runtime/Transports/UTP/UnityTransport.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs index 3f8fbd7171..d95d7d7521 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs @@ -253,6 +253,12 @@ private void InvalidEndpointError() /// /// Endpoint (IP address and port) clients will connect to. /// + /// + /// If a DNS hostname was set as the address, this will return an invalid endpoint. This + /// is still handled correctly by NGO, but for this reason usage of this property is + /// discouraged. + /// + [Obsolete("Use NetworkEndpoint.Parse on the Address field instead.")] public NetworkEndpoint ServerEndPoint { get From 5aa2c63dd3602ae97581b1a852631026b215a8c8 Mon Sep 17 00:00:00 2001 From: Simon Lemay Date: Mon, 11 Aug 2025 15:17:29 -0400 Subject: [PATCH 3/8] fix: Accept single words as valid hostnames --- com.unity.netcode.gameobjects/CHANGELOG.md | 1 + .../Runtime/Transports/UTP/UnityTransport.cs | 114 ++++-------------- .../Editor/Transports/UnityTransportTests.cs | 27 +---- .../UnityTransportConnectionTests.cs | 61 ++++++---- 4 files changed, 61 insertions(+), 142 deletions(-) diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index db93801f9a..9d41c0bde6 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -13,6 +13,7 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Fixed +- Fixed an issue where `UnityTransport` would not accept single words as valid hostnames (notably "localhost"). - Fixed issue where viewing a `NetworkBehaviour` with one or more `NetworkVariable` fields could throw an exception if running a distributed authority network topology with a local (DAHost) host and viewed on the host when the host is not the authority of the associated `NetworkObject`. (#3578) - Fixed issue when using a distributed authority network topology and viewing a `NetworkBehaviour` with one or more `NetworkVariable` fields in the inspector view would not show editable fields. (#3578) diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs index d95d7d7521..d580b3740c 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs @@ -235,7 +235,7 @@ public struct ConnectionAddressData [SerializeField] public string ServerListenAddress; - private static NetworkEndpoint ParseNetworkEndpoint(string ip, ushort port) + internal static NetworkEndpoint ParseNetworkEndpoint(string ip, ushort port) { NetworkEndpoint endpoint = default; if (!NetworkEndpoint.TryParse(ip, port, out endpoint, NetworkFamily.Ipv4)) @@ -245,11 +245,6 @@ private static NetworkEndpoint ParseNetworkEndpoint(string ip, ushort port) return endpoint; } - private void InvalidEndpointError() - { - Debug.LogError($"Invalid network endpoint: {Address}:{Port}."); - } - /// /// Endpoint (IP address and port) clients will connect to. /// @@ -259,23 +254,7 @@ private void InvalidEndpointError() /// discouraged. /// [Obsolete("Use NetworkEndpoint.Parse on the Address field instead.")] - public NetworkEndpoint ServerEndPoint - { - get - { - var networkEndpoint = ParseNetworkEndpoint(Address, Port); - if (networkEndpoint == default) - { -#if HOSTNAME_RESOLUTION_AVAILABLE - if (!IsValidFqdn(Address)) -#endif - { - InvalidEndpointError(); - } - } - return networkEndpoint; - } - } + public NetworkEndpoint ServerEndPoint => ParseNetworkEndpoint(Address, Port); /// /// Endpoint (IP address and port) server will listen/bind on. @@ -287,14 +266,7 @@ public NetworkEndpoint ListenEndPoint NetworkEndpoint endpoint = default; if (string.IsNullOrEmpty(ServerListenAddress)) { - endpoint = NetworkEndpoint.LoopbackIpv4; - - // If an address was entered and it's IPv6, switch to using ::1 as the - // default listen address. (Otherwise we always assume IPv4.) - if (!string.IsNullOrEmpty(Address) && ServerEndPoint.Family == NetworkFamily.Ipv6) - { - endpoint = NetworkEndpoint.LoopbackIpv6; - } + endpoint = IsIpv6 ? NetworkEndpoint.LoopbackIpv6 : NetworkEndpoint.LoopbackIpv4; endpoint = endpoint.WithPort(Port); } else @@ -302,7 +274,7 @@ public NetworkEndpoint ListenEndPoint endpoint = ParseNetworkEndpoint(ServerListenAddress, Port); if (endpoint == default) { - InvalidEndpointError(); + Debug.LogError($"Invalid listen endpoint: {ServerListenAddress}:{Port}. Note that the listen endpoint MUST be an IP address (not a hostname)."); } } return endpoint; @@ -310,9 +282,11 @@ public NetworkEndpoint ListenEndPoint } /// - /// Returns true if the end point address is of type . + /// Returns true if the end point address is of type or + /// if it is a hostname (because in current versions of the engine, hostname resolution + /// prioritizes IPv6 addresses). /// - public bool IsIpv6 => !string.IsNullOrEmpty(Address) && NetworkEndpoint.TryParse(Address, Port, out NetworkEndpoint _, NetworkFamily.Ipv6); + public bool IsIpv6 => !string.IsNullOrEmpty(Address) && !NetworkEndpoint.TryParse(Address, Port, out NetworkEndpoint _, NetworkFamily.Ipv4); } @@ -673,16 +647,6 @@ private NetworkPipeline SelectSendPipeline(NetworkDelivery delivery) } } -#if HOSTNAME_RESOLUTION_AVAILABLE - private static bool IsValidFqdn(string fqdn) - { - // Regular expression to validate FQDN - string pattern = @"^(?=.{1,255}$)(?!-)[A-Za-z0-9-]{1,63}(?A representing the connection to the server, or an invalid connection if the connection attempt fails. protected virtual NetworkConnection Connect(NetworkEndpoint serverEndpoint) { +#if HOSTNAME_RESOLUTION_AVAILABLE + // If the server endpoint is invalid, it means whatever the user entered in the address + // field was not an IP address, and must be presumed to be a hostname. + if (serverEndpoint.Family == NetworkFamily.Invalid) + { + return m_Driver.Connect(ConnectionData.Address, ConnectionData.Port); + } +#endif return m_Driver.Connect(serverEndpoint); } private bool ServerBindAndListen(NetworkEndpoint endPoint) { - // Verify the endpoint is valid before proceeding if (endPoint.Family == NetworkFamily.Invalid) { -#if HOSTNAME_RESOLUTION_AVAILABLE - // If it's not valid, assure it meets FQDN standards - if (!IsValidFqdn(ConnectionData.Address)) - { - // If not then log an error and return false - Debug.LogError($"Listen network address ({ConnectionData.Address}) is not a valid {NetworkFamily.Ipv4} or {NetworkFamily.Ipv6} address!"); - } - else - { - Debug.LogError($"While ({ConnectionData.Address}) is a valid Fully Qualified Domain Name, you must use a valid {NetworkFamily.Ipv4} or {NetworkFamily.Ipv6} address when binding and listening for connections!"); - } return false; -#else - Debug.LogError($"Network listen address ({ConnectionData.Address}) is {nameof(NetworkFamily.Invalid)}!"); - return false; -#endif } InitDriver(); diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs index a7ae940618..bb43739976 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs @@ -127,16 +127,11 @@ public void UnityTransport_RestartSucceedsAfterFailure() UnityTransport transport = new GameObject().AddComponent(); transport.Initialize(); - transport.SetConnectionData("127.0.0.", 4242, "127.0.0."); + transport.SetConnectionData("127.0.0.1", 4242, "foobar"); Assert.False(transport.StartServer()); - LogAssert.Expect(LogType.Error, "Invalid network endpoint: 127.0.0.:4242."); + LogAssert.Expect(LogType.Error, "Invalid listen endpoint: foobar:4242. Note that the listen endpoint MUST be an IP address (not a hostname)."); -#if HOSTNAME_RESOLUTION_AVAILABLE - LogAssert.Expect(LogType.Error, "Listen network address (127.0.0.) is not a valid Ipv4 or Ipv6 address!"); -#else - LogAssert.Expect(LogType.Error, "Network listen address (127.0.0.) is Invalid!"); -#endif transport.SetConnectionData("127.0.0.1", 4242, "127.0.0.1"); Assert.True(transport.StartServer()); @@ -156,24 +151,6 @@ public void UnityTransport_StartServerWithoutAddresses() transport.Shutdown(); } - // Check that StartClient returns false with bad connection data. - [Test] - public void UnityTransport_StartClientFailsWithBadAddress() - { - UnityTransport transport = new GameObject().AddComponent(); - transport.Initialize(); - - transport.SetConnectionData("foobar", 4242); - Assert.False(transport.StartClient()); - LogAssert.Expect(LogType.Error, "Invalid network endpoint: foobar:4242."); -#if HOSTNAME_RESOLUTION_AVAILABLE - LogAssert.Expect(LogType.Error, "Target server network address (foobar) is not a valid Fully Qualified Domain Name!"); -#else - LogAssert.Expect(LogType.Error, "Target server network address (foobar) is Invalid!"); -#endif - transport.Shutdown(); - } - [Test] public void UnityTransport_EmptySecurityStringsShouldThrow([Values("", null)] string cert, [Values("", null)] string secret) { diff --git a/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportConnectionTests.cs b/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportConnectionTests.cs index 03674f9977..d94e7ef0b9 100644 --- a/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportConnectionTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Runtime/Transports/UnityTransportConnectionTests.cs @@ -5,6 +5,7 @@ using NUnit.Framework; using Unity.Netcode.TestHelpers.Runtime; using Unity.Netcode.Transports.UTP; +using Unity.Networking.Transport; using UnityEngine; using UnityEngine.TestTools; using static Unity.Netcode.RuntimeTests.UnityTransportTestHelpers; @@ -55,45 +56,52 @@ public IEnumerator Cleanup() yield return null; } - // Check that invalid endpoint addresses are detected and return false if detected - [Test] - public void DetectInvalidEndpoint() + // Check connection with a single client (IP address). + [UnityTest] + public IEnumerator ConnectSingleClient_IPAddress() { - using var netcodeLogAssert = new NetcodeLogAssert(true); InitializeTransport(out m_Server, out m_ServerEvents); InitializeTransport(out m_Clients[0], out m_ClientsEvents[0]); - m_Server.ConnectionData.Address = "Fubar"; - m_Server.ConnectionData.ServerListenAddress = "Fubar"; - m_Clients[0].ConnectionData.Address = "MoreFubar"; - Assert.False(m_Server.StartServer(), "Server failed to detect invalid endpoint!"); - Assert.False(m_Clients[0].StartClient(), "Client failed to detect invalid endpoint!"); -#if HOSTNAME_RESOLUTION_AVAILABLE - LogAssert.Expect(LogType.Error, $"Listen network address ({m_Server.ConnectionData.Address}) is not a valid {Networking.Transport.NetworkFamily.Ipv4} or {Networking.Transport.NetworkFamily.Ipv6} address!"); - LogAssert.Expect(LogType.Error, $"Target server network address ({m_Clients[0].ConnectionData.Address}) is not a valid Fully Qualified Domain Name!"); - - m_Server.ConnectionData.Address = "my.fubar.com"; - m_Server.ConnectionData.ServerListenAddress = "my.fubar.com"; - Assert.False(m_Server.StartServer(), "Server failed to detect invalid endpoint!"); - LogAssert.Expect(LogType.Error, $"While ({m_Server.ConnectionData.Address}) is a valid Fully Qualified Domain Name, you must use a " + - $"valid {Networking.Transport.NetworkFamily.Ipv4} or {Networking.Transport.NetworkFamily.Ipv6} address when binding and listening for connections!"); -#else - netcodeLogAssert.LogWasReceived(LogType.Error, $"Network listen address ({m_Server.ConnectionData.Address}) is Invalid!"); - netcodeLogAssert.LogWasReceived(LogType.Error, $"Target server network address ({m_Clients[0].ConnectionData.Address}) is Invalid!"); -#endif - UnityTransportTestComponent.CleanUp(); + m_Clients[0].SetConnectionData("127.0.0.1", 7777); + + m_Server.StartServer(); + m_Clients[0].StartClient(); + + yield return WaitForNetworkEvent(NetworkEvent.Connect, m_ClientsEvents[0]); + + // Check we've received Connect event on server too. + Assert.AreEqual(1, m_ServerEvents.Count); + Assert.AreEqual(NetworkEvent.Connect, m_ServerEvents[0].Type); + + yield return null; } - // Check connection with a single client. +#if HOSTNAME_RESOLUTION_AVAILABLE + // Check connection with a single client (hostname). [UnityTest] - public IEnumerator ConnectSingleClient() + public IEnumerator ConnectSingleClient_Hostname() { InitializeTransport(out m_Server, out m_ServerEvents); InitializeTransport(out m_Clients[0], out m_ClientsEvents[0]); - m_Server.StartServer(); + // We don't know if localhost will resolve to 127.0.0.1 or ::1, so we wait until we know + // before starting the server. Because localhost is pretty much always defined locally + // it should resolve immediatly and thus waiting one frame should be enough. + + // We'll need to retry connection requests most likely so make this fast. + m_Clients[0].ConnectTimeoutMS = 50; + + m_Clients[0].SetConnectionData("localhost", 7777); m_Clients[0].StartClient(); + yield return null; + + var endpoint = m_Clients[0].GetLocalEndpoint(); + var ip = endpoint.Family == NetworkFamily.Ipv4 ? "127.0.0.1" : "::1"; + m_Server.SetConnectionData(ip, 7777, ip); + m_Server.StartServer(); + yield return WaitForNetworkEvent(NetworkEvent.Connect, m_ClientsEvents[0]); // Check we've received Connect event on server too. @@ -102,6 +110,7 @@ public IEnumerator ConnectSingleClient() yield return null; } +#endif // Check connection with multiple clients. [UnityTest] From 99b75f905833794852e2368170ed842a379a30f7 Mon Sep 17 00:00:00 2001 From: Simon Lemay Date: Mon, 11 Aug 2025 15:59:22 -0400 Subject: [PATCH 4/8] Add PR number to CHANGELOG entries --- com.unity.netcode.gameobjects/CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/CHANGELOG.md b/com.unity.netcode.gameobjects/CHANGELOG.md index 9d41c0bde6..77862c45f6 100644 --- a/com.unity.netcode.gameobjects/CHANGELOG.md +++ b/com.unity.netcode.gameobjects/CHANGELOG.md @@ -13,12 +13,13 @@ Additional documentation and release notes are available at [Multiplayer Documen ### Fixed -- Fixed an issue where `UnityTransport` would not accept single words as valid hostnames (notably "localhost"). +- Fixed an issue where `UnityTransport` would not accept single words as valid hostnames (notably "localhost"). (#3591) - Fixed issue where viewing a `NetworkBehaviour` with one or more `NetworkVariable` fields could throw an exception if running a distributed authority network topology with a local (DAHost) host and viewed on the host when the host is not the authority of the associated `NetworkObject`. (#3578) - Fixed issue when using a distributed authority network topology and viewing a `NetworkBehaviour` with one or more `NetworkVariable` fields in the inspector view would not show editable fields. (#3578) ### Changed +- Marked `UnityTransport.ConnectionAddressData.ServerEndPoint` as obsolete. It can't work when using hostnames as the server address, and its functionality can easily be replicated using `NetworkEndpoint.Parse`. (#3591) ## [2.5.0] - 2025-08-01 From 71dd4840c6e0d675a070de8dac8b732ed8da6877 Mon Sep 17 00:00:00 2001 From: Simon Lemay Date: Tue, 12 Aug 2025 13:04:23 -0400 Subject: [PATCH 5/8] Remove unnecessary using directive --- .../Runtime/Transports/UTP/UnityTransport.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs index d580b3740c..5bd985df5c 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs @@ -7,9 +7,6 @@ using System; using System.Collections.Generic; -#if HOSTNAME_RESOLUTION_AVAILABLE -using System.Text.RegularExpressions; -#endif using Unity.Burst; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; From 6cc567302e250b7aedc0730702f49ef475e65de4 Mon Sep 17 00:00:00 2001 From: Simon Lemay Date: Thu, 14 Aug 2025 18:13:35 -0400 Subject: [PATCH 6/8] Add validation of hostnames when starting clients --- .../Runtime/Transports/UTP/UnityTransport.cs | 14 ++++++-- .../Editor/Transports/UnityTransportTests.cs | 32 +++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs index 5bd985df5c..596ff58cad 100644 --- a/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs +++ b/com.unity.netcode.gameobjects/Runtime/Transports/UTP/UnityTransport.cs @@ -655,16 +655,24 @@ private bool ClientBindAndConnect() else { // This will result in an invalid endpoint if the address is a hostname. - // This is handled later in the Connect method if hostname resolution is available, + // This is handled later in the Connect method if hostname resolution is available + // (although we still check for hostname validity to error out early if it's not), // but if not then we need to error out here. serverEndpoint = ConnectionAddressData.ParseNetworkEndpoint(ConnectionData.Address, ConnectionData.Port); -#if !HOSTNAME_RESOLUTION_AVAILABLE + if (serverEndpoint.Family == NetworkFamily.Invalid) { +#if HOSTNAME_RESOLUTION_AVAILABLE + if (Uri.CheckHostName(ConnectionData.Address) != UriHostNameType.Dns) + { + Debug.LogError($"Provided connection address \"{ConnectionData.Address}\" is not a valid hostname."); + return false; + } +#else Debug.LogError($"Invalid server address: {ConnectionData.Address}:{ConnectionData.Port}."); return false; - } #endif + } } InitDriver(); diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs index bb43739976..b5579aece4 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs @@ -183,5 +183,37 @@ public void UnityTransport_EmptySecurityStringsShouldThrow([Values("", null)] st } } } + +#if HOSTNAME_RESOLUTION_AVAILABLE + private static readonly (string, bool)[] HostnameChecks = + { + ("localhost", true), + ("unity3d.com", true), + ("unity3d.com.", true), + (string.Empty, false), + ("unity3d.com/test", false), + ("test%123.com", false), + }; + + [Test] + [TestCaseSource(nameof(HostnameChecks))] + public void UnityTransport_HostnameValidation((string, bool) testCases) + { + var (hostname, isValid) = testCases; + + UnityTransport transport = new GameObject().AddComponent(); + transport.Initialize(); + + if (!isValid) + { + LogAssert.Expect(LogType.Error, $"Provided connection address \"{hostname}\" is not a valid hostname."); + } + + transport.SetConnectionData(hostname, 4242); + Assert.AreEqual(isValid, transport.StartClient()); + + transport.Shutdown(); + } +#endif } } From 6216e63ff3683ab7f313a8b4d755cf2831819a5e Mon Sep 17 00:00:00 2001 From: Simon Lemay Date: Thu, 14 Aug 2025 18:19:53 -0400 Subject: [PATCH 7/8] Make the standards check happy --- .../Tests/Editor/Transports/UnityTransportTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs index b5579aece4..d836cd6d73 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs @@ -185,7 +185,7 @@ public void UnityTransport_EmptySecurityStringsShouldThrow([Values("", null)] st } #if HOSTNAME_RESOLUTION_AVAILABLE - private static readonly (string, bool)[] HostnameChecks = + private static readonly (string, bool)[] k_HostnameChecks = { ("localhost", true), ("unity3d.com", true), @@ -197,9 +197,9 @@ private static readonly (string, bool)[] HostnameChecks = [Test] [TestCaseSource(nameof(HostnameChecks))] - public void UnityTransport_HostnameValidation((string, bool) testCases) + public void UnityTransport_HostnameValidation((string, bool) testCase) { - var (hostname, isValid) = testCases; + var (hostname, isValid) = testCase; UnityTransport transport = new GameObject().AddComponent(); transport.Initialize(); From f6cdd9991c958b55c60c34f5c06b96f3721d80fc Mon Sep 17 00:00:00 2001 From: Simon Lemay Date: Thu, 14 Aug 2025 18:20:23 -0400 Subject: [PATCH 8/8] Correctly fix the standards check :P --- .../Tests/Editor/Transports/UnityTransportTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs b/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs index d836cd6d73..7afd0b879d 100644 --- a/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs +++ b/com.unity.netcode.gameobjects/Tests/Editor/Transports/UnityTransportTests.cs @@ -196,7 +196,7 @@ private static readonly (string, bool)[] k_HostnameChecks = }; [Test] - [TestCaseSource(nameof(HostnameChecks))] + [TestCaseSource(nameof(k_HostnameChecks))] public void UnityTransport_HostnameValidation((string, bool) testCase) { var (hostname, isValid) = testCase;