Skip to content

Commit 8645583

Browse files
Remove most unsafe code from SocketAddress (#61006)
Part of the overall "reduce unsafe" effort org-wide.
1 parent afd6f4a commit 8645583

File tree

5 files changed

+50
-65
lines changed

5 files changed

+50
-65
lines changed

Diff for: src/Servers/HttpSys/src/NativeMethods.txt

+2
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,5 @@ HttpSetRequestQueueProperty
5757
HttpSetUrlGroupProperty
5858
HttpSetUrlGroupProperty
5959
SetFileCompletionNotificationModes
60+
SOCKADDR_IN
61+
SOCKADDR_IN6

Diff for: src/Servers/IIS/IIS/src/NativeMethods.txt

+2
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,5 @@ HTTP_REQUEST_PROPERTY_SNI_HOST_MAX_LENGTH
1414
HTTP_REQUEST_V1
1515
HTTP_REQUEST_V2
1616
HTTP_SSL_PROTOCOL_INFO
17+
SOCKADDR_IN
18+
SOCKADDR_IN6

Diff for: src/Shared/HttpSys/NativeInterop/SocketAddress.cs

+41-64
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,73 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System.Diagnostics.Contracts;
54
using System.Net;
6-
using System.Net.Sockets;
5+
using Windows.Win32.Networking.WinSock;
76

87
namespace Microsoft.AspNetCore.HttpSys.Internal;
98

10-
internal sealed class SocketAddress
9+
internal abstract class SocketAddress
1110
{
12-
private const int NumberOfIPv6Labels = 8;
13-
private const int IPv6AddressSize = 28;
14-
private const int IPv4AddressSize = 16;
15-
private const int WriteableOffset = 2;
11+
internal abstract int GetPort();
1612

17-
private readonly byte[] _buffer;
18-
private readonly int _size;
13+
internal abstract IPAddress? GetIPAddress();
1914

20-
private SocketAddress(AddressFamily family, int size)
15+
internal static unsafe SocketAddress? CopyOutAddress(SOCKADDR* pSockaddr)
2116
{
22-
ArgumentOutOfRangeException.ThrowIfLessThan(size, WriteableOffset);
23-
Family = family;
24-
_size = size;
25-
// Sized to match the native structure
26-
_buffer = new byte[((size / IntPtr.Size) + 2) * IntPtr.Size]; // sizeof DWORD
17+
// Per https://learn.microsoft.com/windows/win32/api/ws2def/ns-ws2def-sockaddr,
18+
// use the SOCKADDR* pointer only to read the address family, then cast the pointer to
19+
// the appropriate family type and continue processing.
20+
return pSockaddr->sa_family switch
21+
{
22+
ADDRESS_FAMILY.AF_INET => new SocketAddressIPv4(*(SOCKADDR_IN*)pSockaddr),
23+
ADDRESS_FAMILY.AF_INET6 => new SocketAddressIPv6(*(SOCKADDR_IN6*)pSockaddr),
24+
_ => null
25+
};
2726
}
2827

29-
internal AddressFamily Family { get; }
30-
31-
internal int GetPort()
28+
private sealed class SocketAddressIPv4 : SocketAddress
3229
{
33-
return (_buffer[2] << 8 & 0xFF00) | (_buffer[3]);
34-
}
30+
private readonly SOCKADDR_IN _sockaddr;
3531

36-
internal IPAddress? GetIPAddress()
37-
{
38-
if (Family == AddressFamily.InterNetworkV6)
32+
internal SocketAddressIPv4(in SOCKADDR_IN sockaddr)
3933
{
40-
return GetIpv6Address();
34+
_sockaddr = sockaddr;
4135
}
42-
else if (Family == AddressFamily.InterNetwork)
36+
37+
internal override int GetPort()
4338
{
44-
return GetIPv4Address();
39+
// sin_port is network byte order
40+
return IPAddress.NetworkToHostOrder((short)_sockaddr.sin_port);
4541
}
46-
else
42+
43+
internal override IPAddress? GetIPAddress()
4744
{
48-
return null;
45+
// address is network byte order
46+
return new IPAddress(_sockaddr.sin_addr.S_un.S_addr);
4947
}
5048
}
5149

52-
private IPAddress GetIpv6Address()
53-
{
54-
Contract.Assert(_size >= IPv6AddressSize);
55-
var bytes = new byte[NumberOfIPv6Labels * 2];
56-
Array.Copy(_buffer, 8, bytes, 0, NumberOfIPv6Labels * 2);
57-
return new IPAddress(bytes); // TODO: Does scope id matter?
58-
}
59-
60-
private IPAddress GetIPv4Address()
50+
private sealed class SocketAddressIPv6 : SocketAddress
6151
{
62-
Contract.Assert(_size >= IPv4AddressSize);
63-
return new IPAddress(new byte[] { _buffer[4], _buffer[5], _buffer[6], _buffer[7] });
64-
}
52+
private readonly SOCKADDR_IN6 _sockaddr;
6553

66-
internal static unsafe SocketAddress? CopyOutAddress(IntPtr address)
67-
{
68-
var addressFamily = *((ushort*)address);
69-
if (addressFamily == (ushort)AddressFamily.InterNetwork)
54+
internal SocketAddressIPv6(in SOCKADDR_IN6 sockaddr)
7055
{
71-
var v4address = new SocketAddress(AddressFamily.InterNetwork, IPv4AddressSize);
72-
fixed (byte* pBuffer = v4address._buffer)
73-
{
74-
for (var index = 2; index < IPv4AddressSize; index++)
75-
{
76-
pBuffer[index] = ((byte*)address)[index];
77-
}
78-
}
79-
return v4address;
56+
_sockaddr = sockaddr;
8057
}
81-
if (addressFamily == (ushort)AddressFamily.InterNetworkV6)
58+
59+
internal override int GetPort()
8260
{
83-
var v6address = new SocketAddress(AddressFamily.InterNetworkV6, IPv6AddressSize);
84-
fixed (byte* pBuffer = v6address._buffer)
85-
{
86-
for (var index = 2; index < IPv6AddressSize; index++)
87-
{
88-
pBuffer[index] = ((byte*)address)[index];
89-
}
90-
}
91-
return v6address;
61+
// sin6_port is network byte order
62+
return IPAddress.NetworkToHostOrder((short)_sockaddr.sin6_port);
9263
}
9364

94-
return null;
65+
internal override IPAddress? GetIPAddress()
66+
{
67+
// address is network byte order
68+
// when CsWin32 gets support for inline arrays, remove 'AsReadOnlySpan' call below.
69+
// https://github.com/microsoft/CsWin32/issues/1086
70+
return new IPAddress(_sockaddr.sin6_addr.u.Byte.AsReadOnlySpan()); // TODO: Does scope id matter?
71+
}
9572
}
9673
}

Diff for: src/Shared/HttpSys/RequestProcessing/NativeRequestContext.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using Microsoft.Extensions.Primitives;
1818
using Windows.Win32;
1919
using Windows.Win32.Networking.HttpServer;
20+
using Windows.Win32.Networking.WinSock;
2021

2122
namespace Microsoft.AspNetCore.HttpSys.Internal;
2223

@@ -656,7 +657,8 @@ private void GetUnknownHeadersHelper(IDictionary<string, StringValues> unknownHe
656657
{
657658
return null;
658659
}
659-
var address = (IntPtr)(pMemoryBlob + _bufferAlignment - (byte*)_originalBufferAddress + source);
660+
661+
var address = (SOCKADDR*)(pMemoryBlob + _bufferAlignment - (byte*)_originalBufferAddress + source);
660662
return SocketAddress.CopyOutAddress(address);
661663
}
662664

Diff for: src/Shared/test/Shared.Tests/NativeMethods.txt

+2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
// https://github.com/microsoft/cswin32
22
Windows.Win32.Networking.HttpServer
3+
SOCKADDR_IN
4+
SOCKADDR_IN6

0 commit comments

Comments
 (0)