Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto-enable SO_REUSE_UNICASTPORT for ConnectEx #54903

Merged
merged 5 commits into from
Jul 8, 2021
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace System.Net.Sockets
{
public partial class Socket
{
private static readonly bool s_osSupportsReuseUnicastPort = IsReuseUnicastPortSupported();
private static CachedSerializedEndPoint? s_cachedAnyEndPoint;
private static CachedSerializedEndPoint? s_cachedAnyV6EndPoint;
private static CachedSerializedEndPoint? s_cachedMappedAnyV6EndPoint;
Expand Down Expand Up @@ -235,6 +236,13 @@ partial void WildcardBindForConnectIfNecessary(AddressFamily addressFamily)

if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, csep.IPEndPoint);

// By enabling SO_REUSE_UNICASTPORT, we defer actual port allocation until the ConnectEx call,
// enabling to bind to ports from the Windows auto-reuse port range, if configured by an admin.
if (_socketType == SocketType.Stream && _protocolType == ProtocolType.Tcp && s_osSupportsReuseUnicastPort)
{
SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseUnicastPort, true);
}

DoBind(csep.IPEndPoint, csep.SocketAddress);
}

Expand Down Expand Up @@ -412,5 +420,29 @@ internal ThreadPoolBoundHandle GetOrAllocateThreadPoolBoundHandleSlow()
bool trySkipCompletionPortOnSuccess = !(CompletionPortHelper.PlatformHasUdpIssue && _protocolType == ProtocolType.Udp);
return _handle.GetOrAllocateThreadPoolBoundHandle(trySkipCompletionPortOnSuccess);
}

private static bool IsReuseUnicastPortSupported()
{
Interop.Winsock.EnsureInitialized();

try
{
IntPtr socket = Interop.Winsock.WSASocketW(AddressFamily.InterNetwork, SocketType.Stream, 0, IntPtr.Zero, 0, (int)Interop.Winsock.SocketConstructorFlags.WSA_FLAG_NO_HANDLE_INHERIT);
using var handle = new SafeSocketHandle(socket, ownsHandle: true);
if (handle.IsInvalid)
return false;

int optionValue = 1;
SocketError error = Interop.Winsock.setsockopt(
handle,
SocketOptionLevel.Socket,
SocketOptionName.ReuseUnicastPort,
ref optionValue,
sizeof(int));
return error == SocketError.Success;
}
catch { }
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used try-catch block because SocketProtocolSupportPal does the same:

private static bool IsSupported(AddressFamily af)
{
Interop.Winsock.EnsureInitialized();
IntPtr INVALID_SOCKET = (IntPtr)(-1);
IntPtr socket = INVALID_SOCKET;
try
{
socket = Interop.Winsock.WSASocketW(af, SocketType.Stream, 0, IntPtr.Zero, 0, (int)Interop.Winsock.SocketConstructorFlags.WSA_FLAG_NO_HANDLE_INHERIT);
return
socket != INVALID_SOCKET ||
(SocketError)Marshal.GetLastWin32Error() != SocketError.AddressFamilyNotSupported;
}
finally
{
if (socket != INVALID_SOCKET)
{
Interop.Winsock.closesocket(socket);
}
}
}

Don't know what can potentially throw here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For native calls like this, usually the only thing that can throw is ObjectDisposedException if the SafeHandle is disposed. Maybe if it's invalid too.

That should never be the case here, but there's no harm in the try/catch just in case.

return false;
}
}
}