@@ -22,13 +22,19 @@ public partial class Socket : IDisposable
2222 {
2323 internal const int DefaultCloseTimeout = - 1 ; // NOTE: changing this default is a breaking change.
2424
25+ private static readonly IPAddress s_IPAddressAnyMapToIPv6 = IPAddress . Any . MapToIPv6 ( ) ;
26+
2527 private SafeSocketHandle _handle ;
2628
2729 // _rightEndPoint is null if the socket has not been bound. Otherwise, it is any EndPoint of the
2830 // correct type (IPEndPoint, etc).
2931 internal EndPoint ? _rightEndPoint ;
3032 internal EndPoint ? _remoteEndPoint ;
3133
34+ // Cached LocalEndPoint value. Cleared on disconnect and error. Cached wildcard addresses are
35+ // also cleared on connect and accept.
36+ private EndPoint ? _localEndPoint ;
37+
3238 // These flags monitor if the socket was ever connected at any time and if it still is.
3339 private bool _isConnected ;
3440 private bool _isDisconnected ;
@@ -317,6 +323,7 @@ public EndPoint? LocalEndPoint
317323 // Update the state if we've become connected after a non-blocking connect.
318324 _isConnected = true ;
319325 _rightEndPoint = _nonBlockingConnectRightEndPoint ;
326+ UpdateLocalEndPointOnConnect ( ) ;
320327 _nonBlockingConnectInProgress = false ;
321328 }
322329
@@ -325,23 +332,27 @@ public EndPoint? LocalEndPoint
325332 return null ;
326333 }
327334
328- Internals . SocketAddress socketAddress = IPEndPointExtensions . Serialize ( _rightEndPoint ) ;
329-
330- unsafe
335+ if ( _localEndPoint == null )
331336 {
332- fixed ( byte * buffer = socketAddress . Buffer )
333- fixed ( int * bufferSize = & socketAddress . InternalSize )
337+ Internals . SocketAddress socketAddress = IPEndPointExtensions . Serialize ( _rightEndPoint ) ;
338+
339+ unsafe
334340 {
335- // This may throw ObjectDisposedException.
336- SocketError errorCode = SocketPal . GetSockName ( _handle , buffer , bufferSize ) ;
337- if ( errorCode != SocketError . Success )
341+ fixed ( byte * buffer = socketAddress . Buffer )
342+ fixed ( int * bufferSize = & socketAddress . InternalSize )
338343 {
339- UpdateStatusAfterSocketErrorAndThrowException ( errorCode ) ;
344+ // This may throw ObjectDisposedException.
345+ SocketError errorCode = SocketPal . GetSockName ( _handle , buffer , bufferSize ) ;
346+ if ( errorCode != SocketError . Success )
347+ {
348+ UpdateStatusAfterSocketErrorAndThrowException ( errorCode ) ;
349+ }
340350 }
341351 }
352+ _localEndPoint = _rightEndPoint . Create ( socketAddress ) ;
342353 }
343354
344- return _rightEndPoint . Create ( socketAddress ) ;
355+ return _localEndPoint ;
345356 }
346357 }
347358
@@ -359,6 +370,7 @@ public EndPoint? RemoteEndPoint
359370 // Update the state if we've become connected after a non-blocking connect.
360371 _isConnected = true ;
361372 _rightEndPoint = _nonBlockingConnectRightEndPoint ;
373+ UpdateLocalEndPointOnConnect ( ) ;
362374 _nonBlockingConnectInProgress = false ;
363375 }
364376
@@ -470,6 +482,7 @@ public bool Connected
470482 // Update the state if we've become connected after a non-blocking connect.
471483 _isConnected = true ;
472484 _rightEndPoint = _nonBlockingConnectRightEndPoint ;
485+ UpdateLocalEndPointOnConnect ( ) ;
473486 _nonBlockingConnectInProgress = false ;
474487 }
475488
@@ -2303,6 +2316,7 @@ private void DoBeginDisconnect(bool reuseSocket, DisconnectOverlappedAsyncResult
23032316 {
23042317 SetToDisconnected ( ) ;
23052318 _remoteEndPoint = null ;
2319+ _localEndPoint = null ;
23062320 }
23072321
23082322 if ( NetEventSource . Log . IsEnabled ( ) ) NetEventSource . Info ( this , $ "UnsafeNclNativeMethods.OSSOCK.DisConnectEx returns:{ errorCode } ") ;
@@ -2332,6 +2346,7 @@ public void Disconnect(bool reuseSocket)
23322346
23332347 SetToDisconnected ( ) ;
23342348 _remoteEndPoint = null ;
2349+ _localEndPoint = null ;
23352350 }
23362351
23372352 // Routine Description:
@@ -2760,6 +2775,7 @@ private void DoBeginSendTo(byte[] buffer, int offset, int size, SocketFlags sock
27602775 catch ( ObjectDisposedException )
27612776 {
27622777 _rightEndPoint = oldEndPoint ;
2778+ _localEndPoint = null ;
27632779 throw ;
27642780 }
27652781
@@ -2769,6 +2785,7 @@ private void DoBeginSendTo(byte[] buffer, int offset, int size, SocketFlags sock
27692785 UpdateSendSocketErrorForDisposed ( ref errorCode ) ;
27702786 // Update the internal state of this socket according to the error before throwing.
27712787 _rightEndPoint = oldEndPoint ;
2788+ _localEndPoint = null ;
27722789
27732790 throw new SocketException ( ( int ) errorCode ) ;
27742791 }
@@ -3148,6 +3165,7 @@ public IAsyncResult BeginReceiveMessageFrom(byte[] buffer, int offset, int size,
31483165 catch ( ObjectDisposedException )
31493166 {
31503167 _rightEndPoint = oldEndPoint ;
3168+ _localEndPoint = null ;
31513169 throw ;
31523170 }
31533171
@@ -3157,6 +3175,7 @@ public IAsyncResult BeginReceiveMessageFrom(byte[] buffer, int offset, int size,
31573175 {
31583176 // Update the internal state of this socket according to the error before throwing.
31593177 _rightEndPoint = oldEndPoint ;
3178+ _localEndPoint = null ;
31603179
31613180 throw new SocketException ( ( int ) errorCode ) ;
31623181 }
@@ -3357,6 +3376,7 @@ private void DoBeginReceiveFrom(byte[] buffer, int offset, int size, SocketFlags
33573376 catch ( ObjectDisposedException )
33583377 {
33593378 _rightEndPoint = oldEndPoint ;
3379+ _localEndPoint = null ;
33603380 throw ;
33613381 }
33623382
@@ -3366,6 +3386,7 @@ private void DoBeginReceiveFrom(byte[] buffer, int offset, int size, SocketFlags
33663386 {
33673387 // Update the internal state of this socket according to the error before throwing.
33683388 _rightEndPoint = oldEndPoint ;
3389+ _localEndPoint = null ;
33693390
33703391 throw new SocketException ( ( int ) errorCode ) ;
33713392 }
@@ -3762,6 +3783,7 @@ private bool ConnectAsync(SocketAsyncEventArgs e, bool userSocket)
37623783 catch
37633784 {
37643785 _rightEndPoint = oldEndPoint ;
3786+ _localEndPoint = null ;
37653787
37663788 // Clear in-use flag on event args object.
37673789 e . Complete ( ) ;
@@ -4091,6 +4113,7 @@ public bool SendToAsync(SocketAsyncEventArgs e)
40914113 catch
40924114 {
40934115 _rightEndPoint = null ;
4116+ _localEndPoint = null ;
40944117 // Clear in-use flag on event args object.
40954118 e . Complete ( ) ;
40964119 throw ;
@@ -4099,6 +4122,7 @@ public bool SendToAsync(SocketAsyncEventArgs e)
40994122 if ( ! CheckErrorAndUpdateStatus ( socketError ) )
41004123 {
41014124 _rightEndPoint = oldEndPoint ;
4125+ _localEndPoint = null ;
41024126 }
41034127
41044128 return socketError == SocketError . IOPending ;
@@ -4619,6 +4643,7 @@ private IAsyncResult BeginConnectEx(EndPoint remoteEP, bool flowContext, AsyncCa
46194643 {
46204644 // _rightEndPoint will always equal oldEndPoint.
46214645 _rightEndPoint = oldEndPoint ;
4646+ _localEndPoint = null ;
46224647 throw ;
46234648 }
46244649
@@ -4635,6 +4660,7 @@ private IAsyncResult BeginConnectEx(EndPoint remoteEP, bool flowContext, AsyncCa
46354660 UpdateConnectSocketErrorForDisposed ( ref errorCode ) ;
46364661 // Update the internal state of this socket according to the error before throwing.
46374662 _rightEndPoint = oldEndPoint ;
4663+ _localEndPoint = null ;
46384664
46394665 throw new SocketException ( ( int ) errorCode ) ;
46404666 }
@@ -4858,6 +4884,12 @@ internal Socket UpdateAcceptSocket(Socket socket, EndPoint remoteEP)
48584884 socket . _rightEndPoint = _rightEndPoint ;
48594885 socket . _remoteEndPoint = remoteEP ;
48604886
4887+ // If the listener socket was bound to a wildcard address, then the `accept` system call
4888+ // will assign a specific address to the accept socket's local endpoint instead of a
4889+ // wildcard address. In that case we should not copy listener's wildcard local endpoint.
4890+
4891+ socket . _localEndPoint = ! IsWildcardEndPoint ( _localEndPoint ) ? _localEndPoint : null ;
4892+
48614893 // The socket is connected.
48624894 socket . SetToConnected ( ) ;
48634895
@@ -4889,9 +4921,38 @@ internal void SetToConnected()
48894921 // some point in time update the perf counter as well.
48904922 _isConnected = true ;
48914923 _isDisconnected = false ;
4924+ UpdateLocalEndPointOnConnect ( ) ;
48924925 if ( NetEventSource . Log . IsEnabled ( ) ) NetEventSource . Info ( this , "now connected" ) ;
48934926 }
48944927
4928+ private void UpdateLocalEndPointOnConnect ( )
4929+ {
4930+ // If the client socket was bound to a wildcard address, then the `connect` system call
4931+ // will assign a specific address to the client socket's local endpoint instead of a
4932+ // wildcard address. In that case we should clear the cached wildcard local endpoint.
4933+
4934+ if ( IsWildcardEndPoint ( _localEndPoint ) )
4935+ {
4936+ _localEndPoint = null ;
4937+ }
4938+ }
4939+
4940+ private bool IsWildcardEndPoint ( EndPoint ? endPoint )
4941+ {
4942+ if ( endPoint == null )
4943+ {
4944+ return false ;
4945+ }
4946+
4947+ if ( endPoint is IPEndPoint ipEndpoint )
4948+ {
4949+ IPAddress address = ipEndpoint . Address ;
4950+ return IPAddress . Any . Equals ( address ) || IPAddress . IPv6Any . Equals ( address ) || s_IPAddressAnyMapToIPv6 . Equals ( address ) ;
4951+ }
4952+
4953+ return false ;
4954+ }
4955+
48954956 internal void SetToDisconnected ( )
48964957 {
48974958 if ( ! _isConnected )
0 commit comments