Skip to content

Commit

Permalink
Merge branch 'develop' into feature_AES_CSP
Browse files Browse the repository at this point in the history
  • Loading branch information
zybexXL authored Dec 14, 2021
2 parents d231717 + 7bdfc9e commit 9efeb24
Show file tree
Hide file tree
Showing 31 changed files with 1,895 additions and 95 deletions.
3 changes: 3 additions & 0 deletions src/Renci.SshNet.Silverlight/Renci.SshNet.Silverlight.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -1358,6 +1358,9 @@
<Compile Include="..\Renci.SshNet\Sftp\SftpFile.cs">
<Link>Sftp\SftpFile.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\Sftp\ISftpFile.cs">
<Link>Sftp\ISftpFile.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\Sftp\SftpFileAttributes.cs">
<Link>Sftp\SftpFileAttributes.cs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1364,6 +1364,9 @@
<Compile Include="..\Renci.SshNet\Sftp\SftpFile.cs">
<Link>Sftp\SftpFile.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\Sftp\ISftpFile.cs">
<Link>Sftp\ISftpFile.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\Sftp\SftpFileAttributes.cs">
<Link>Sftp\SftpFileAttributes.cs</Link>
</Compile>
Expand Down
12 changes: 6 additions & 6 deletions src/Renci.SshNet.Tests/Classes/SftpClientTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -562,8 +562,8 @@ public void EndListDirectoryTest()
ConnectionInfo connectionInfo = null; // TODO: Initialize to an appropriate value
SftpClient target = new SftpClient(connectionInfo); // TODO: Initialize to an appropriate value
IAsyncResult asyncResult = null; // TODO: Initialize to an appropriate value
IEnumerable<SftpFile> expected = null; // TODO: Initialize to an appropriate value
IEnumerable<SftpFile> actual;
IEnumerable<ISftpFile> expected = null; // TODO: Initialize to an appropriate value
IEnumerable<ISftpFile> actual;
actual = target.EndListDirectory(asyncResult);
Assert.AreEqual(expected, actual);
Assert.Inconclusive("Verify the correctness of this test method.");
Expand Down Expand Up @@ -702,8 +702,8 @@ public void GetTest()
ConnectionInfo connectionInfo = null; // TODO: Initialize to an appropriate value
SftpClient target = new SftpClient(connectionInfo); // TODO: Initialize to an appropriate value
string path = string.Empty; // TODO: Initialize to an appropriate value
SftpFile expected = null; // TODO: Initialize to an appropriate value
SftpFile actual;
ISftpFile expected = null; // TODO: Initialize to an appropriate value
ISftpFile actual;
actual = target.Get(path);
Assert.AreEqual(expected, actual);
Assert.Inconclusive("Verify the correctness of this test method.");
Expand Down Expand Up @@ -802,8 +802,8 @@ public void ListDirectoryTest()
SftpClient target = new SftpClient(connectionInfo); // TODO: Initialize to an appropriate value
string path = string.Empty; // TODO: Initialize to an appropriate value
Action<int> listCallback = null; // TODO: Initialize to an appropriate value
IEnumerable<SftpFile> expected = null; // TODO: Initialize to an appropriate value
IEnumerable<SftpFile> actual;
IEnumerable<ISftpFile> expected = null; // TODO: Initialize to an appropriate value
IEnumerable<ISftpFile> actual;
actual = target.ListDirectory(path, listCallback);
Assert.AreEqual(expected, actual);
Assert.Inconclusive("Verify the correctness of this test method.");
Expand Down
3 changes: 3 additions & 0 deletions src/Renci.SshNet.UAP10/Renci.SshNet.UAP10.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -1440,6 +1440,9 @@
<Compile Include="..\Renci.SshNet\Sftp\SftpFile.cs">
<Link>Sftp\SftpFile.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\Sftp\ISftpFile.cs">
<Link>Sftp\ISftpFile.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\Sftp\SftpFileAttributes.cs">
<Link>Sftp\SftpFileAttributes.cs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1343,6 +1343,9 @@
<Compile Include="..\Renci.SshNet\Sftp\SftpFile.cs">
<Link>Sftp\SftpFile.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\Sftp\ISftpFile.cs">
<Link>Sftp\ISftpFile.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\Sftp\SftpFileAttributes.cs">
<Link>Sftp\SftpFileAttributes.cs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1396,6 +1396,9 @@
<Compile Include="..\Renci.SshNet\Sftp\SftpFile.cs">
<Link>Sftp\SftpFile.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\Sftp\ISftpFile.cs">
<Link>Sftp\ISftpFile.cs</Link>
</Compile>
<Compile Include="..\Renci.SshNet\Sftp\SftpFileAttributes.cs">
<Link>Sftp\SftpFileAttributes.cs</Link>
</Compile>
Expand Down
22 changes: 22 additions & 0 deletions src/Renci.SshNet/Abstractions/DnsAbstraction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
using System.Net;
using System.Net.Sockets;

#if FEATURE_TAP
using System.Threading.Tasks;
#endif

#if FEATURE_DNS_SYNC
#elif FEATURE_DNS_APM
using Renci.SshNet.Common;
Expand Down Expand Up @@ -87,5 +91,23 @@ public static IPAddress[] GetHostAddresses(string hostNameOrAddress)
#endif // FEATURE_DEVICEINFORMATION_APM
#endif
}

#if FEATURE_TAP
/// <summary>
/// Returns the Internet Protocol (IP) addresses for the specified host.
/// </summary>
/// <param name="hostNameOrAddress">The host name or IP address to resolve</param>
/// <returns>
/// A task with result of an array of type <see cref="IPAddress"/> that holds the IP addresses for the host that
/// is specified by the <paramref name="hostNameOrAddress"/> parameter.
/// </returns>
/// <exception cref="ArgumentNullException"><paramref name="hostNameOrAddress"/> is <c>null</c>.</exception>
/// <exception cref="SocketException">An error is encountered when resolving <paramref name="hostNameOrAddress"/>.</exception>
public static Task<IPAddress[]> GetHostAddressesAsync(string hostNameOrAddress)
{
return Dns.GetHostAddressesAsync(hostNameOrAddress);
}
#endif

}
}
17 changes: 17 additions & 0 deletions src/Renci.SshNet/Abstractions/SocketAbstraction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
using System.Net;
using System.Net.Sockets;
using System.Threading;
#if FEATURE_TAP
using System.Threading.Tasks;
#endif
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Transport;

Expand Down Expand Up @@ -59,6 +62,13 @@ public static void Connect(Socket socket, IPEndPoint remoteEndpoint, TimeSpan co
ConnectCore(socket, remoteEndpoint, connectTimeout, false);
}

#if FEATURE_TAP
public static Task ConnectAsync(Socket socket, IPEndPoint remoteEndpoint, CancellationToken cancellationToken)
{
return socket.ConnectAsync(remoteEndpoint, cancellationToken);
}
#endif

private static void ConnectCore(Socket socket, IPEndPoint remoteEndpoint, TimeSpan connectTimeout, bool ownsSocket)
{
#if FEATURE_SOCKET_EAP
Expand Down Expand Up @@ -317,6 +327,13 @@ public static byte[] Read(Socket socket, int size, TimeSpan timeout)
return buffer;
}

#if FEATURE_TAP
public static Task<int> ReadAsync(Socket socket, byte[] buffer, int offset, int length, CancellationToken cancellationToken)
{
return socket.ReceiveAsync(buffer, offset, length, cancellationToken);
}
#endif

/// <summary>
/// Receives data from a bound <see cref="Socket"/> into a receive buffer.
/// </summary>
Expand Down
119 changes: 119 additions & 0 deletions src/Renci.SshNet/Abstractions/SocketExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
#if FEATURE_TAP
using System;
using System.Net;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;

namespace Renci.SshNet.Abstractions
{
// Async helpers based on https://devblogs.microsoft.com/pfxteam/awaiting-socket-operations/

internal static class SocketExtensions
{
sealed class SocketAsyncEventArgsAwaitable : SocketAsyncEventArgs, INotifyCompletion
{
private readonly static Action SENTINEL = () => { };

private bool isCancelled;
private Action continuationAction;

public SocketAsyncEventArgsAwaitable()
{
Completed += delegate { SetCompleted(); };
}

public SocketAsyncEventArgsAwaitable ExecuteAsync(Func<SocketAsyncEventArgs, bool> func)
{
if (!func(this))
{
SetCompleted();
}
return this;
}

public void SetCompleted()
{
IsCompleted = true;
var continuation = continuationAction ?? Interlocked.CompareExchange(ref continuationAction, SENTINEL, null);
if (continuation != null)
{
continuation();
}
}

public void SetCancelled()
{
isCancelled = true;
SetCompleted();
}

public SocketAsyncEventArgsAwaitable GetAwaiter() { return this; }

public bool IsCompleted { get; private set; }

void INotifyCompletion.OnCompleted(Action continuation)
{
if (continuationAction == SENTINEL || Interlocked.CompareExchange(ref continuationAction, continuation, null) == SENTINEL)
{
// We have already completed; run continuation asynchronously
Task.Run(continuation);
}
}

public void GetResult()
{
if (isCancelled)
{
throw new TaskCanceledException();
}
else if (IsCompleted)
{
if (SocketError != SocketError.Success)
{
throw new SocketException((int)SocketError);
}
}
else
{
// We don't support sync/async
throw new InvalidOperationException("The asynchronous operation has not yet completed.");
}
}
}

public static async Task ConnectAsync(this Socket socket, IPEndPoint remoteEndpoint, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();

using (var args = new SocketAsyncEventArgsAwaitable())
{
args.RemoteEndPoint = remoteEndpoint;

using (cancellationToken.Register(o => ((SocketAsyncEventArgsAwaitable)o).SetCancelled(), args, false))
{
await args.ExecuteAsync(socket.ConnectAsync);
}
}
}

public static async Task<int> ReceiveAsync(this Socket socket, byte[] buffer, int offset, int length, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();

using (var args = new SocketAsyncEventArgsAwaitable())
{
args.SetBuffer(buffer, offset, length);

using (cancellationToken.Register(o => ((SocketAsyncEventArgsAwaitable)o).SetCancelled(), args, false))
{
await args.ExecuteAsync(socket.ReceiveAsync);
}

return args.BytesTransferred;
}
}
}
}
#endif
9 changes: 7 additions & 2 deletions src/Renci.SshNet/Abstractions/ThreadAbstraction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,17 @@ public static void Sleep(int millisecondsTimeout)

public static void ExecuteThreadLongRunning(Action action)
{
if (action == null)
throw new ArgumentNullException("action");

#if FEATURE_THREAD_TAP
var taskCreationOptions = System.Threading.Tasks.TaskCreationOptions.LongRunning;
System.Threading.Tasks.Task.Factory.StartNew(action, taskCreationOptions);
#else
var thread = new System.Threading.Thread(() => action());
thread.Start();
new System.Threading.Thread(() => action())
{
IsBackground = true
}.Start();
#endif
}

Expand Down
80 changes: 80 additions & 0 deletions src/Renci.SshNet/BaseClient.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using System;
using System.Net.Sockets;
using System.Threading;
#if FEATURE_TAP
using System.Threading.Tasks;
#endif
using Renci.SshNet.Abstractions;
using Renci.SshNet.Common;
using Renci.SshNet.Messages.Transport;
Expand Down Expand Up @@ -239,6 +242,63 @@ public void Connect()
StartKeepAliveTimer();
}

#if FEATURE_TAP
/// <summary>
/// Asynchronously connects client to the server.
/// </summary>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe.</param>
/// <returns>A <see cref="Task"/> that represents the asynchronous connect operation.
/// </returns>
/// <exception cref="InvalidOperationException">The client is already connected.</exception>
/// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
/// <exception cref="SocketException">Socket connection to the SSH server or proxy server could not be established, or an error occurred while resolving the hostname.</exception>
/// <exception cref="SshConnectionException">SSH session could not be established.</exception>
/// <exception cref="SshAuthenticationException">Authentication of SSH session failed.</exception>
/// <exception cref="ProxyException">Failed to establish proxy connection.</exception>
public async Task ConnectAsync(CancellationToken cancellationToken)
{
CheckDisposed();
cancellationToken.ThrowIfCancellationRequested();

// TODO (see issue #1758):
// we're not stopping the keep-alive timer and disposing the session here
//
// we could do this but there would still be side effects as concrete
// implementations may still hang on to the original session
//
// therefore it would be better to actually invoke the Disconnect method
// (and then the Dispose on the session) but even that would have side effects
// eg. it would remove all forwarded ports from SshClient
//
// I think we should modify our concrete clients to better deal with a
// disconnect. In case of SshClient this would mean not removing the
// forwarded ports on disconnect (but only on dispose ?) and link a
// forwarded port with a client instead of with a session
//
// To be discussed with Oleg (or whoever is interested)
if (IsSessionConnected())
throw new InvalidOperationException("The client is already connected.");

OnConnecting();

Session = await CreateAndConnectSessionAsync(cancellationToken).ConfigureAwait(false);
try
{
// Even though the method we invoke makes you believe otherwise, at this point only
// the SSH session itself is connected.
OnConnected();
}
catch
{
// Only dispose the session as Disconnect() would have side-effects (such as remove forwarded
// ports in SshClient).
DisposeSession();
throw;
}
StartKeepAliveTimer();
}
#endif

/// <summary>
/// Disconnects client from the server.
/// </summary>
Expand Down Expand Up @@ -473,6 +533,26 @@ private ISession CreateAndConnectSession()
}
}

#if FEATURE_TAP
private async Task<ISession> CreateAndConnectSessionAsync(CancellationToken cancellationToken)
{
var session = _serviceFactory.CreateSession(ConnectionInfo, _serviceFactory.CreateSocketFactory());
session.HostKeyReceived += Session_HostKeyReceived;
session.ErrorOccured += Session_ErrorOccured;

try
{
await session.ConnectAsync(cancellationToken).ConfigureAwait(false);
return session;
}
catch
{
DisposeSession(session);
throw;
}
}
#endif

private void DisposeSession(ISession session)
{
session.ErrorOccured -= Session_ErrorOccured;
Expand Down
Loading

0 comments on commit 9efeb24

Please sign in to comment.