diff --git a/src/Servers/HttpSys/HttpSysServer.slnf b/src/Servers/HttpSys/HttpSysServer.slnf index 7c081bb272c4..b9171033dd0d 100644 --- a/src/Servers/HttpSys/HttpSysServer.slnf +++ b/src/Servers/HttpSys/HttpSysServer.slnf @@ -55,4 +55,4 @@ "src\\WebEncoders\\src\\Microsoft.Extensions.WebEncoders.csproj" ] } -} \ No newline at end of file +} diff --git a/src/Servers/HttpSys/test/Tests/NativeInterop/SocketAddressTests.cs b/src/Servers/HttpSys/test/Tests/NativeInterop/SocketAddressTests.cs new file mode 100644 index 000000000000..7304f66c0d3d --- /dev/null +++ b/src/Servers/HttpSys/test/Tests/NativeInterop/SocketAddressTests.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Net; +using Microsoft.AspNetCore.HttpSys.Internal; +using Windows.Win32.Networking.WinSock; +using Xunit; +using static Microsoft.AspNetCore.HttpSys.Internal.SocketAddress; +using SocketAddress = Microsoft.AspNetCore.HttpSys.Internal.SocketAddress; + +namespace Microsoft.AspNetCore.Server.HttpSys.Tests.NativeInterop; + +public class SocketAddressTests +{ + [Theory] + [InlineData(80)] + [InlineData(443)] + [InlineData(8080)] + [InlineData(32767)] // max signed short + [InlineData(32768)] // min value that causes negative when cast to short + [InlineData(42000)] + [InlineData(65535)] // Max port number + public void IPv6_GetPort_ReturnsCorrectPort_ForAllValidPorts(ushort expectedPort) + { + var nativeIpV6Address = new SOCKADDR_IN6 + { + sin6_family = ADDRESS_FAMILY.AF_INET6, + sin6_port = (ushort)IPAddress.HostToNetworkOrder((short)expectedPort) + }; + + var socketAddress = new SocketAddressIPv6(nativeIpV6Address); + var actualPort = socketAddress.GetPort(); + + Assert.Equal(expectedPort, actualPort); + Assert.True(actualPort >= 0, "Port should never be negative"); + Assert.True(actualPort <= 65535, "Port should not exceed maximum valid port"); + } + + [Theory] + [InlineData(80)] + [InlineData(443)] + [InlineData(8080)] + [InlineData(42000)] + [InlineData(32767)] + [InlineData(32768)] + [InlineData(65535)] + public void IPv4_GetPort_ReturnsCorrectPort_ForAllValidPorts(ushort expectedPort) + { + var nativeIpV4Address = new SOCKADDR_IN + { + sin_family = ADDRESS_FAMILY.AF_INET, + sin_port = (ushort)IPAddress.HostToNetworkOrder((short)expectedPort) + }; + + var socketAddress = new SocketAddressIPv4(nativeIpV4Address); + var actualPort = socketAddress.GetPort(); + + Assert.Equal(expectedPort, actualPort); + Assert.True(actualPort >= 0, "Port should never be negative"); + Assert.True(actualPort <= 65535, "Port should not exceed maximum valid port"); + } +} diff --git a/src/Shared/HttpSys/NativeInterop/SocketAddress.cs b/src/Shared/HttpSys/NativeInterop/SocketAddress.cs index 9c3e7136e30b..b38af58c0638 100644 --- a/src/Shared/HttpSys/NativeInterop/SocketAddress.cs +++ b/src/Shared/HttpSys/NativeInterop/SocketAddress.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers.Binary; using System.Net; using Windows.Win32.Networking.WinSock; @@ -25,7 +26,8 @@ internal abstract class SocketAddress }; } - private sealed class SocketAddressIPv4 : SocketAddress + // internal for testing + internal sealed class SocketAddressIPv4 : SocketAddress { private readonly SOCKADDR_IN _sockaddr; @@ -36,8 +38,9 @@ internal SocketAddressIPv4(in SOCKADDR_IN sockaddr) internal override int GetPort() { - // sin_port is network byte order - return IPAddress.NetworkToHostOrder((short)_sockaddr.sin_port); + // _sockaddr.sin_port has network byte order. + // cast to ushort is important to avoid negative values for the TCP port + return (ushort)IPAddress.NetworkToHostOrder((short)_sockaddr.sin_port); } internal override IPAddress? GetIPAddress() @@ -47,7 +50,8 @@ internal override int GetPort() } } - private sealed class SocketAddressIPv6 : SocketAddress + // internal for testing + internal sealed class SocketAddressIPv6 : SocketAddress { private readonly SOCKADDR_IN6 _sockaddr; @@ -58,8 +62,9 @@ internal SocketAddressIPv6(in SOCKADDR_IN6 sockaddr) internal override int GetPort() { - // sin6_port is network byte order - return IPAddress.NetworkToHostOrder((short)_sockaddr.sin6_port); + // _sockaddr.sin6_port has network byte order. + // cast to ushort is important to avoid negative values for the TCP port + return (ushort)IPAddress.NetworkToHostOrder((short)_sockaddr.sin6_port); } internal override IPAddress? GetIPAddress()