-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #14 from mehrdadn/master
Add WSL support via UnixSocket
- Loading branch information
Showing
9 changed files
with
496 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
namespace System.Net.Sockets | ||
{ | ||
/// <summary>Represents a Unix Domain Socket endpoint as a path.</summary> | ||
public sealed partial class UnixDomainSocketEndPoint : EndPoint | ||
{ | ||
#pragma warning disable CA1802 // on Unix these need to be static readonly rather than const, so we do the same on Windows for consistency | ||
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) | ||
#pragma warning restore CA1802 | ||
|
||
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Diagnostics; | ||
using System.Text; | ||
using System.IO; | ||
|
||
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 readonly string _path; | ||
private readonly byte[] _encodedPath; | ||
|
||
// Tracks the file Socket should delete on Dispose. | ||
internal string BoundFileName { get; } | ||
|
||
public UnixDomainSocketEndPoint(string path) | ||
: this(path, null) | ||
{ } | ||
|
||
private UnixDomainSocketEndPoint(string path, string boundFileName) | ||
{ | ||
if (path == null) | ||
{ | ||
throw new ArgumentNullException(nameof(path)); | ||
} | ||
|
||
BoundFileName = boundFileName; | ||
|
||
// 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 = Encoding.UTF8.GetByteCount(path); | ||
if (!isAbstract) | ||
{ | ||
// for null terminator | ||
bufferLength++; | ||
} | ||
|
||
if (path.Length == 0 || bufferLength > s_nativePathLength) | ||
{ | ||
const string ArgumentOutOfRange_PathLengthInvalid = | ||
"The path '{0}' is of an invalid length for use with domain sockets on this platform. The length must be between 1 and {1} characters, inclusive."; | ||
|
||
throw new ArgumentOutOfRangeException( | ||
nameof(path), path, | ||
string.Format(ArgumentOutOfRange_PathLengthInvalid, path, s_nativePathLength)); | ||
//SR.Format(SR.ArgumentOutOfRange_PathLengthInvalid, path, s_nativePathLength)); | ||
} | ||
|
||
_path = path; | ||
_encodedPath = new byte[bufferLength]; | ||
int bytesEncoded = Encoding.UTF8.GetBytes(path, 0, path.Length, _encodedPath, 0); | ||
Debug.Assert(bufferLength - (isAbstract ? 0 : 1) == bytesEncoded); | ||
|
||
// FIXME: see https://github.com/dotnet/runtime/blob/f85ea976f81945ea18cd5dc71959cccecdc93cd2/src/libraries/Common/src/System/Net/SocketProtocolSupportPal.Windows.cs#L14 | ||
//if (!Socket.OSSupportsUnixDomainSockets) | ||
//{ | ||
// throw new PlatformNotSupportedException(); | ||
//} | ||
} | ||
|
||
internal static int MaxAddressSize => s_nativeAddressSize; | ||
|
||
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 = Encoding.UTF8.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 string.Concat("@", _path.AsSpan(1)); | ||
return "@" + _path.Substring(1); | ||
} | ||
else | ||
{ | ||
return _path; | ||
} | ||
} | ||
|
||
internal UnixDomainSocketEndPoint CreateBoundEndPoint() | ||
{ | ||
if (IsAbstract(_path)) | ||
{ | ||
return this; | ||
} | ||
return new UnixDomainSocketEndPoint(_path, Path.GetFullPath(_path)); | ||
} | ||
|
||
internal UnixDomainSocketEndPoint CreateUnboundEndPoint() | ||
{ | ||
if (IsAbstract(_path) || BoundFileName is null) | ||
{ | ||
return this; | ||
} | ||
return new UnixDomainSocketEndPoint(_path, null); | ||
} | ||
|
||
private static bool IsAbstract(string path) => path.Length > 0 && path[0] == '\0'; | ||
|
||
private static bool IsAbstract(byte[] encodedPath) => encodedPath.Length > 0 && encodedPath[0] == 0; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
// | ||
// | ||
// PageantAgent.cs | ||
// | ||
// Author(s): David Lechner <[email protected]> | ||
|
@@ -53,6 +53,7 @@ public class PageantAgent : Agent | |
const int ERROR_CLASS_ALREADY_EXISTS = 1410; | ||
const int WM_COPYDATA = 0x004A; | ||
const int WSAECONNABORTED = 10053; | ||
const int WSAECONNRESET = 10054; | ||
|
||
/* From PuTTY source code */ | ||
|
||
|
@@ -70,6 +71,7 @@ public class PageantAgent : Agent | |
object lockObject = new object(); | ||
CygwinSocket cygwinSocket; | ||
MsysSocket msysSocket; | ||
WslSocket wslSocket; | ||
WindowsOpenSshPipe opensshPipe; | ||
Thread winThread; | ||
|
||
|
@@ -287,6 +289,36 @@ public void StopMsysSocket() | |
msysSocket = null; | ||
} | ||
|
||
/// <summary> | ||
/// Starts a wsl style socket that can be used by the ssh program | ||
/// that comes with wsl. | ||
/// </summary> | ||
/// <param name="path">The path to the socket file that will be created.</param> | ||
public void StartWslSocket(string path) | ||
{ | ||
if (disposed) { | ||
throw new ObjectDisposedException("PagentAgent"); | ||
} | ||
if (wslSocket != null) { | ||
return; | ||
} | ||
// only overwrite a file if it looks like a WslSocket file. | ||
if (File.Exists(path) && WslSocket.TestFile(path)) { | ||
File.Delete(path); | ||
} | ||
wslSocket = new WslSocket(path, connectionHandler); | ||
} | ||
|
||
public void StopWslSocket() | ||
{ | ||
if (disposed) | ||
throw new ObjectDisposedException("PagentAgent"); | ||
if (wslSocket == null) | ||
return; | ||
wslSocket.Dispose(); | ||
wslSocket = null; | ||
} | ||
|
||
public void StartWindowsOpenSshPipe() | ||
{ | ||
if (disposed) { | ||
|
@@ -353,6 +385,7 @@ private void RunWindowInNewAppcontext() | |
// make sure socket files are cleaned up when we stop. | ||
StopCygwinSocket(); | ||
StopMsysSocket(); | ||
StopWslSocket(); | ||
StopWindowsOpenSshPipe(); | ||
|
||
if (hwnd != IntPtr.Zero) { | ||
|
@@ -455,7 +488,8 @@ void connectionHandler(Stream stream, Process process) | |
} | ||
} catch (IOException ex) { | ||
var socketException = ex.InnerException as SocketException; | ||
if (socketException != null && socketException.ErrorCode == WSAECONNABORTED) { | ||
if (socketException != null && ( | ||
socketException.ErrorCode == WSAECONNABORTED || socketException.ErrorCode == WSAECONNRESET)) { | ||
// expected error | ||
return; | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.