Skip to content

Commit

Permalink
Support UnixDomainSocketEndPoint on Windows (dotnet/corefx#27631)
Browse files Browse the repository at this point in the history
* Support UnixDomainSocketEndPoint on Windows

Windows is adding support for Unix domain sockets, and it's easy to enable that support via our new UnixDomainSocketEndPoint type.

* Address PR feedback


Commit migrated from dotnet/corefx@421ce7b
  • Loading branch information
stephentoub committed Mar 2, 2018
1 parent b8711e2 commit e7cafc0
Show file tree
Hide file tree
Showing 7 changed files with 216 additions and 181 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
<Compile Include="System\Net\Sockets\MultipleConnectAsync.cs" />
<Compile Include="System\Net\Sockets\OverlappedAsyncResult.cs" />
<Compile Include="System\Net\Sockets\ReceiveMessageOverlappedAsyncResult.cs" />
<Compile Include="System\Net\Sockets\UnixDomainSocketEndPoint.cs" />
<Compile Include="$(CommonPath)\System\IO\StreamHelpers.CopyValidation.cs">
<Link>Common\System\IO\StreamHelpers.CopyValidation.cs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,12 @@
namespace System.Net.Sockets
{
/// <summary>Represents a Unix Domain Socket endpoint as a path.</summary>
public sealed class UnixDomainSocketEndPoint : EndPoint
public sealed partial class UnixDomainSocketEndPoint : EndPoint
{
private const AddressFamily EndPointAddressFamily = AddressFamily.Unix;

private static readonly Encoding s_pathEncoding = Encoding.UTF8;
private static readonly int s_nativePathOffset;
private static readonly int s_nativePathLength;
private static readonly int s_nativeAddressSize;

private readonly string _path;
private readonly byte[] _encodedPath;

static UnixDomainSocketEndPoint()
{
Interop.Sys.GetDomainSocketSizes(out s_nativePathOffset, out s_nativePathLength, out s_nativeAddressSize);
Expand All @@ -29,107 +23,7 @@ static UnixDomainSocketEndPoint()
Debug.Assert(s_nativePathLength >= 92, "Expected max path length to be at least 92"); // per http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_un.h.html
}

public UnixDomainSocketEndPoint(string path)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}

// Pathname socket addresses should be null-terminated.
// Linux abstract socket addresses start with a zero byte, they must not be null-terminated.
bool isAbstract = IsAbstract(path);
int bufferLength = s_pathEncoding.GetByteCount(path);
if (!isAbstract)
{
// for null terminator
bufferLength++;
}

if (path.Length == 0 || bufferLength > s_nativePathLength)
{
throw new ArgumentOutOfRangeException(
nameof(path), path,
SR.Format(SR.ArgumentOutOfRange_PathLengthInvalid, path, s_nativePathLength));
}

_path = path;
_encodedPath = new byte[bufferLength];
int bytesEncoded = s_pathEncoding.GetBytes(path, 0, path.Length, _encodedPath, 0);
Debug.Assert(bufferLength - (isAbstract ? 0 : 1) == bytesEncoded);
}

internal UnixDomainSocketEndPoint(SocketAddress socketAddress)
{
if (socketAddress == null)
{
throw new ArgumentNullException(nameof(socketAddress));
}

if (socketAddress.Family != EndPointAddressFamily ||
socketAddress.Size > s_nativeAddressSize)
{
throw new ArgumentOutOfRangeException(nameof(socketAddress));
}

if (socketAddress.Size > s_nativePathOffset)
{
_encodedPath = new byte[socketAddress.Size - s_nativePathOffset];
for (int i = 0; i < _encodedPath.Length; i++)
{
_encodedPath[i] = socketAddress[s_nativePathOffset + i];
}

// Strip trailing null of pathname socket addresses.
int length = _encodedPath.Length;
if (!IsAbstract(_encodedPath))
{
// Since this isn't an abstract path, we're sure our first byte isn't 0.
while (_encodedPath[length - 1] == 0)
{
length--;
}
}
_path = s_pathEncoding.GetString(_encodedPath, 0, length);
}
else
{
_encodedPath = Array.Empty<byte>();
_path = string.Empty;
}
}

public override SocketAddress Serialize()
{
var result = new SocketAddress(AddressFamily.Unix, s_nativePathOffset + _encodedPath.Length);

for (int index = 0; index < _encodedPath.Length; index++)
{
result[s_nativePathOffset + index] = _encodedPath[index];
}

return result;
}

public override EndPoint Create(SocketAddress socketAddress) => new UnixDomainSocketEndPoint(socketAddress);

public override AddressFamily AddressFamily => EndPointAddressFamily;

public override string ToString()
{
bool isAbstract = IsAbstract(_path);
if (isAbstract)
{
return "@" + _path.Substring(1);
}
else
{
return _path;
}
}

private static bool IsAbstract(string path) => path.Length > 0 && path[0] == '\0';

private static bool IsAbstract(byte[] encodedPath) => encodedPath.Length > 0 && encodedPath[0] == 0;
private SocketAddress CreateSocketAddressForSerialize() =>
new SocketAddress(AddressFamily.Unix, s_nativePathOffset + _encodedPath.Length);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,22 @@
namespace System.Net.Sockets
{
/// <summary>Represents a Unix Domain Socket endpoint as a path.</summary>
public sealed class UnixDomainSocketEndPoint : EndPoint
public sealed partial class UnixDomainSocketEndPoint : EndPoint
{
public UnixDomainSocketEndPoint(string path)
{
throw new PlatformNotSupportedException();
}
private static readonly int s_nativePathOffset = 2; // sizeof(sun_family)
private static readonly int s_nativePathLength = 108; // sizeof(sun_path)
private static readonly int s_nativeAddressSize = s_nativePathOffset + s_nativePathLength; // sizeof(sockaddr_un)

private SocketAddress CreateSocketAddressForSerialize() =>
new SocketAddress(AddressFamily.Unix, s_nativeAddressSize);

// from afunix.h:
//#define UNIX_PATH_MAX 108
//typedef struct sockaddr_un
//{
// ADDRESS_FAMILY sun_family; /* AF_UNIX */
// char sun_path[UNIX_PATH_MAX]; /* pathname */
//}
//SOCKADDR_UN, *PSOCKADDR_UN;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Diagnostics;
using System.Text;

namespace System.Net.Sockets
{
/// <summary>Represents a Unix Domain Socket endpoint as a path.</summary>
public sealed partial class UnixDomainSocketEndPoint : EndPoint
{
private const AddressFamily EndPointAddressFamily = AddressFamily.Unix;

private static readonly Encoding s_pathEncoding = Encoding.UTF8;
private static readonly Lazy<bool> s_udsSupported = new Lazy<bool>(() =>
{
try
{
new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified).Dispose();
return true;
}
catch
{
return false;
}
});

private readonly string _path;
private readonly byte[] _encodedPath;

public UnixDomainSocketEndPoint(string path)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}

// Pathname socket addresses should be null-terminated.
// Linux abstract socket addresses start with a zero byte, they must not be null-terminated.
bool isAbstract = IsAbstract(path);
int bufferLength = s_pathEncoding.GetByteCount(path);
if (!isAbstract)
{
// for null terminator
bufferLength++;
}

if (path.Length == 0 || bufferLength > s_nativePathLength)
{
throw new ArgumentOutOfRangeException(
nameof(path), path,
SR.Format(SR.ArgumentOutOfRange_PathLengthInvalid, path, s_nativePathLength));
}

_path = path;
_encodedPath = new byte[bufferLength];
int bytesEncoded = s_pathEncoding.GetBytes(path, 0, path.Length, _encodedPath, 0);
Debug.Assert(bufferLength - (isAbstract ? 0 : 1) == bytesEncoded);

if (!s_udsSupported.Value)
{
throw new PlatformNotSupportedException();
}
}

internal UnixDomainSocketEndPoint(SocketAddress socketAddress)
{
if (socketAddress == null)
{
throw new ArgumentNullException(nameof(socketAddress));
}

if (socketAddress.Family != EndPointAddressFamily ||
socketAddress.Size > s_nativeAddressSize)
{
throw new ArgumentOutOfRangeException(nameof(socketAddress));
}

if (socketAddress.Size > s_nativePathOffset)
{
_encodedPath = new byte[socketAddress.Size - s_nativePathOffset];
for (int i = 0; i < _encodedPath.Length; i++)
{
_encodedPath[i] = socketAddress[s_nativePathOffset + i];
}

// Strip trailing null of pathname socket addresses.
int length = _encodedPath.Length;
if (!IsAbstract(_encodedPath))
{
// Since this isn't an abstract path, we're sure our first byte isn't 0.
while (_encodedPath[length - 1] == 0)
{
length--;
}
}
_path = s_pathEncoding.GetString(_encodedPath, 0, length);
}
else
{
_encodedPath = Array.Empty<byte>();
_path = string.Empty;
}
}

public override SocketAddress Serialize()
{
SocketAddress result = CreateSocketAddressForSerialize();

for (int index = 0; index < _encodedPath.Length; index++)
{
result[s_nativePathOffset + index] = _encodedPath[index];
}

return result;
}

public override EndPoint Create(SocketAddress socketAddress) => new UnixDomainSocketEndPoint(socketAddress);

public override AddressFamily AddressFamily => EndPointAddressFamily;

public override string ToString()
{
bool isAbstract = IsAbstract(_path);
if (isAbstract)
{
return "@" + _path.Substring(1);
}
else
{
return _path;
}
}

private static bool IsAbstract(string path) => path.Length > 0 && path[0] == '\0';

private static bool IsAbstract(byte[] encodedPath) => encodedPath.Length > 0 && encodedPath[0] == 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
<Compile Include="SocketOptionNameTest.cs" />
<Compile Include="MulticastOptionTest.cs" />
<Compile Include="UdpClientTest.cs" />
<Compile Include="UnixDomainSocketTest.cs" />
<Compile Include="UnixDomainSocketTest.netcoreapp.cs" Condition="'$(TargetGroup)' != 'netstandard'" />
<!-- Common Sockets files -->
<Compile Include="$(CommonTestPath)\System\Net\Configuration.cs">
Expand Down

This file was deleted.

Loading

0 comments on commit e7cafc0

Please sign in to comment.