Skip to content

Commit d3ed5a9

Browse files
authored
Socket: delete unix local endpoint filename on Close (#52103)
Fixes #45537
1 parent 93cf5df commit d3ed5a9

File tree

4 files changed

+226
-183
lines changed

4 files changed

+226
-183
lines changed

src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.UnixDomainSockets.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ public async Task NamedPipeClient_Connects_With_UnixDomainSocketEndPointServer()
4949
}
5050
}
5151

52-
Assert.True(File.Exists(pipeName));
53-
try { File.Delete(pipeName); } catch { }
52+
Assert.False(File.Exists(pipeName));
5453
}
5554
}
5655
}

src/libraries/System.Net.Sockets/src/System/Net/Sockets/Socket.cs

+49-41
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@ public partial class Socket : IDisposable
2525

2626
private SafeSocketHandle _handle;
2727

28-
// _rightEndPoint is null if the socket has not been bound. Otherwise, it is any EndPoint of the
29-
// correct type (IPEndPoint, etc).
28+
// _rightEndPoint is null if the socket has not been bound. Otherwise, it is an EndPoint of the
29+
// correct type (IPEndPoint, etc). The Bind operation sets _rightEndPoint. Other operations must only set
30+
// it when the value is still null.
31+
// This enables tracking the file created by UnixDomainSocketEndPoint when the Socket is bound,
32+
// and to delete that file when the Socket gets disposed.
3033
internal EndPoint? _rightEndPoint;
3134
internal EndPoint? _remoteEndPoint;
3235

@@ -284,7 +287,7 @@ public EndPoint? LocalEndPoint
284287
{
285288
// Update the state if we've become connected after a non-blocking connect.
286289
_isConnected = true;
287-
_rightEndPoint = _nonBlockingConnectRightEndPoint;
290+
_rightEndPoint ??= _nonBlockingConnectRightEndPoint;
288291
UpdateLocalEndPointOnConnect();
289292
_nonBlockingConnectInProgress = false;
290293
}
@@ -331,7 +334,7 @@ public EndPoint? RemoteEndPoint
331334
{
332335
// Update the state if we've become connected after a non-blocking connect.
333336
_isConnected = true;
334-
_rightEndPoint = _nonBlockingConnectRightEndPoint;
337+
_rightEndPoint ??= _nonBlockingConnectRightEndPoint;
335338
UpdateLocalEndPointOnConnect();
336339
_nonBlockingConnectInProgress = false;
337340
}
@@ -438,7 +441,7 @@ public bool Connected
438441
{
439442
// Update the state if we've become connected after a non-blocking connect.
440443
_isConnected = true;
441-
_rightEndPoint = _nonBlockingConnectRightEndPoint;
444+
_rightEndPoint ??= _nonBlockingConnectRightEndPoint;
442445
UpdateLocalEndPointOnConnect();
443446
_nonBlockingConnectInProgress = false;
444447
}
@@ -799,11 +802,11 @@ private void DoBind(EndPoint endPointSnapshot, Internals.SocketAddress socketAdd
799802
UpdateStatusAfterSocketErrorAndThrowException(errorCode);
800803
}
801804

802-
if (_rightEndPoint == null)
803-
{
804-
// Save a copy of the EndPoint so we can use it for Create().
805-
_rightEndPoint = endPointSnapshot;
806-
}
805+
// Save a copy of the EndPoint so we can use it for Create().
806+
// For UnixDomainSocketEndPoint, track the file to delete on Dispose.
807+
_rightEndPoint = endPointSnapshot is UnixDomainSocketEndPoint unixEndPoint ?
808+
unixEndPoint.CreateBoundEndPoint() :
809+
endPointSnapshot;
807810
}
808811

809812
// Establishes a connection to a remote system.
@@ -1357,11 +1360,8 @@ public int SendTo(byte[] buffer, int offset, int size, SocketFlags socketFlags,
13571360
if (SocketType == SocketType.Dgram) SocketsTelemetry.Log.DatagramSent();
13581361
}
13591362

1360-
if (_rightEndPoint == null)
1361-
{
1362-
// Save a copy of the EndPoint so we can use it for Create().
1363-
_rightEndPoint = remoteEP;
1364-
}
1363+
// Save a copy of the EndPoint so we can use it for Create().
1364+
_rightEndPoint ??= remoteEP;
13651365

13661366
if (NetEventSource.Log.IsEnabled()) NetEventSource.DumpBuffer(this, buffer, offset, size);
13671367
return bytesTransferred;
@@ -1639,11 +1639,8 @@ public int ReceiveMessageFrom(byte[] buffer, int offset, int size, ref SocketFla
16391639
catch
16401640
{
16411641
}
1642-
if (_rightEndPoint == null)
1643-
{
1644-
// Save a copy of the EndPoint so we can use it for Create().
1645-
_rightEndPoint = endPointSnapshot;
1646-
}
1642+
// Save a copy of the EndPoint so we can use it for Create().
1643+
_rightEndPoint ??= endPointSnapshot;
16471644
}
16481645

16491646
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, errorCode);
@@ -1733,11 +1730,8 @@ public int ReceiveMessageFrom(Span<byte> buffer, ref SocketFlags socketFlags, re
17331730
catch
17341731
{
17351732
}
1736-
if (_rightEndPoint == null)
1737-
{
1738-
// Save a copy of the EndPoint so we can use it for Create().
1739-
_rightEndPoint = endPointSnapshot;
1740-
}
1733+
// Save a copy of the EndPoint so we can use it for Create().
1734+
_rightEndPoint ??= endPointSnapshot;
17411735
}
17421736

17431737
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, errorCode);
@@ -1796,11 +1790,8 @@ public int ReceiveFrom(byte[] buffer, int offset, int size, SocketFlags socketFl
17961790
catch
17971791
{
17981792
}
1799-
if (_rightEndPoint == null)
1800-
{
1801-
// Save a copy of the EndPoint so we can use it for Create().
1802-
_rightEndPoint = endPointSnapshot;
1803-
}
1793+
// Save a copy of the EndPoint so we can use it for Create().
1794+
_rightEndPoint ??= endPointSnapshot;
18041795
}
18051796

18061797
if (socketException != null)
@@ -3121,10 +3112,7 @@ private bool SendToAsync(SocketAsyncEventArgs e, CancellationToken cancellationT
31213112
e.StartOperationCommon(this, SocketAsyncOperation.SendTo);
31223113

31233114
EndPoint? oldEndPoint = _rightEndPoint;
3124-
if (_rightEndPoint == null)
3125-
{
3126-
_rightEndPoint = endPointSnapshot;
3127-
}
3115+
_rightEndPoint ??= endPointSnapshot;
31283116

31293117
SocketError socketError;
31303118
try
@@ -3133,7 +3121,7 @@ private bool SendToAsync(SocketAsyncEventArgs e, CancellationToken cancellationT
31333121
}
31343122
catch
31353123
{
3136-
_rightEndPoint = null;
3124+
_rightEndPoint = oldEndPoint;
31373125
_localEndPoint = null;
31383126
// Clear in-use flag on event args object.
31393127
e.Complete();
@@ -3229,11 +3217,8 @@ private void DoConnect(EndPoint endPointSnapshot, Internals.SocketAddress socket
32293217

32303218
if (SocketsTelemetry.Log.IsEnabled()) SocketsTelemetry.Log.AfterConnect(SocketError.Success);
32313219

3232-
if (_rightEndPoint == null)
3233-
{
3234-
// Save a copy of the EndPoint so we can use it for Create().
3235-
_rightEndPoint = endPointSnapshot;
3236-
}
3220+
// Save a copy of the EndPoint so we can use it for Create().
3221+
_rightEndPoint ??= endPointSnapshot;
32373222

32383223
if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"connection to:{endPointSnapshot}");
32393224

@@ -3361,6 +3346,18 @@ protected virtual void Dispose(bool disposing)
33613346
{
33623347
}
33633348
}
3349+
3350+
// Delete file of bound UnixDomainSocketEndPoint.
3351+
if (_rightEndPoint is UnixDomainSocketEndPoint unixEndPoint &&
3352+
unixEndPoint.BoundFileName is not null)
3353+
{
3354+
try
3355+
{
3356+
File.Delete(unixEndPoint.BoundFileName);
3357+
}
3358+
catch
3359+
{ }
3360+
}
33643361
}
33653362

33663363
// Clean up any cached data
@@ -3615,9 +3612,20 @@ internal Socket UpdateAcceptSocket(Socket socket, EndPoint remoteEP)
36153612
socket._addressFamily = _addressFamily;
36163613
socket._socketType = _socketType;
36173614
socket._protocolType = _protocolType;
3618-
socket._rightEndPoint = _rightEndPoint;
36193615
socket._remoteEndPoint = remoteEP;
36203616

3617+
// If the _rightEndpoint tracks a UnixDomainSocketEndPoint to delete
3618+
// then create a new EndPoint.
3619+
if (_rightEndPoint is UnixDomainSocketEndPoint unixEndPoint &&
3620+
unixEndPoint.BoundFileName is not null)
3621+
{
3622+
socket._rightEndPoint = unixEndPoint.CreateUnboundEndPoint();
3623+
}
3624+
else
3625+
{
3626+
socket._rightEndPoint = _rightEndPoint;
3627+
}
3628+
36213629
// If the listener socket was bound to a wildcard address, then the `accept` system call
36223630
// will assign a specific address to the accept socket's local endpoint instead of a
36233631
// wildcard address. In that case we should not copy listener's wildcard local endpoint.

src/libraries/System.Net.Sockets/src/System/Net/Sockets/UnixDomainSocketEndPoint.cs

+28
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Diagnostics;
55
using System.Text;
6+
using System.IO;
67

78
namespace System.Net.Sockets
89
{
@@ -14,13 +15,22 @@ public sealed partial class UnixDomainSocketEndPoint : EndPoint
1415
private readonly string _path;
1516
private readonly byte[] _encodedPath;
1617

18+
// Tracks the file Socket should delete on Dispose.
19+
internal string? BoundFileName { get; }
20+
1721
public UnixDomainSocketEndPoint(string path)
22+
: this(path, null)
23+
{ }
24+
25+
private UnixDomainSocketEndPoint(string path, string? boundFileName)
1826
{
1927
if (path == null)
2028
{
2129
throw new ArgumentNullException(nameof(path));
2230
}
2331

32+
BoundFileName = boundFileName;
33+
2434
// Pathname socket addresses should be null-terminated.
2535
// Linux abstract socket addresses start with a zero byte, they must not be null-terminated.
2636
bool isAbstract = IsAbstract(path);
@@ -120,6 +130,24 @@ public override string ToString()
120130
}
121131
}
122132

133+
internal UnixDomainSocketEndPoint CreateBoundEndPoint()
134+
{
135+
if (IsAbstract(_path))
136+
{
137+
return this;
138+
}
139+
return new UnixDomainSocketEndPoint(_path, Path.GetFullPath(_path));
140+
}
141+
142+
internal UnixDomainSocketEndPoint CreateUnboundEndPoint()
143+
{
144+
if (IsAbstract(_path) || BoundFileName is null)
145+
{
146+
return this;
147+
}
148+
return new UnixDomainSocketEndPoint(_path, null);
149+
}
150+
123151
private static bool IsAbstract(string path) => path.Length > 0 && path[0] == '\0';
124152

125153
private static bool IsAbstract(byte[] encodedPath) => encodedPath.Length > 0 && encodedPath[0] == 0;

0 commit comments

Comments
 (0)