//--------------------------------------------------------------------- // Helper methods. //--------------------------------------------------------------------- protected static SshSession CreateSession() { var session = new SshSession(); session.SetTraceHandler( LIBSSH2_TRACE.SOCKET | LIBSSH2_TRACE.ERROR | LIBSSH2_TRACE.CONN | LIBSSH2_TRACE.AUTH | LIBSSH2_TRACE.KEX, Console.WriteLine); session.Timeout = TimeSpan.FromSeconds(5); return(session); }
private void WorkerThreadProc() { // // NB. libssh2 has limited support for multi-threading and in general, // it's best to use a libssh2 session from a single thread only. // Therefore, all libssh2 operations are performed by this one thead. // using (SshTraceSources.Default.TraceMethod().WithoutParameters()) { try { using (var session = new SshSession()) { session.SetTraceHandler( LIBSSH2_TRACE.SOCKET | LIBSSH2_TRACE.ERROR | LIBSSH2_TRACE.CONN | LIBSSH2_TRACE.AUTH | LIBSSH2_TRACE.KEX, SshTraceSources.Default.TraceVerbose); if (!string.IsNullOrEmpty(this.Banner)) { session.SetLocalBanner(this.Banner); } session.Timeout = this.ConnectionTimeout; // // Open connection and perform handshake using blocking I/O. // // TODO: Configure algorithms. // using (var connectedSession = session.Connect(this.endpoint)) using (var authenticatedSession = connectedSession.Authenticate( this.username, this.key, this.OnKeyboardInteractivePromptCallback)) using (var channel = CreateChannel(authenticatedSession)) { // // With the channel established, switch to non-blocking I/O. // Use a disposable scope to make sure that tearing down the // connection is done using blocking I/O again. // using (session.AsNonBlocking()) using (var readyToReceive = UnsafeNativeMethods.WSACreateEvent()) { // // Create an event that is signalled whenever there is data // available to read on the socket. // // NB. This is a manual-reset event that must be reset by // calling WSAEnumNetworkEvents. // if (UnsafeNativeMethods.WSAEventSelect( connectedSession.Socket.Handle, readyToReceive, UnsafeNativeMethods.FD_READ) != 0) { throw new Win32Exception( UnsafeNativeMethods.WSAGetLastError(), "WSAEventSelect failed"); } // TODO: Check host keys. // // Looks good so far, consider the connection successful. // OnConnected(); // // Set up keepalives. Because we use non-blocking I/O, we have to // send keepalives by ourselves. // // NB. This method must not be called before the handshake has completed. // session.ConfigureKeepAlive(false, this.KeepAliveInterval); var waitHandles = new[] { readyToReceive.DangerousGetHandle(), this.readyToSend.DangerousGetHandle() }; while (!this.workerCancellationSource.IsCancellationRequested) { var currentOperation = Operation.Receiving | Operation.Sending; try { // // In each iteration, wait for // (data received on socket) OR (user data to send) // // NB. The timeout should not be lower than approx. // one second, otherwise we spend too much time calling // libssh2's keepalive function, which causes the terminal // to become sluggish. // var waitResult = UnsafeNativeMethods.WSAWaitForMultipleEvents( (uint)waitHandles.Length, waitHandles, false, (uint)this.SocketWaitInterval.TotalMilliseconds, false); if (waitResult == UnsafeNativeMethods.WSA_WAIT_EVENT_0) { // // Socket has data available. // currentOperation = Operation.Receiving; // // Reset the WSA event. // var wsaEvents = new UnsafeNativeMethods.WSANETWORKEVENTS() { iErrorCode = new int[10] }; if (UnsafeNativeMethods.WSAEnumNetworkEvents( connectedSession.Socket.Handle, readyToReceive, ref wsaEvents) != 0) { throw new Win32Exception( UnsafeNativeMethods.WSAGetLastError(), "WSAEnumNetworkEvents failed"); } // // Perform whatever receiving operation we need to do. // Receive(channel); } else if (waitResult == UnsafeNativeMethods.WSA_WAIT_EVENT_0 + 1) { // // User has data to send. Perform whatever send operation // we need to do. // currentOperation = Operation.Sending; Send(channel); } else if (waitResult == UnsafeNativeMethods.WSA_WAIT_TIMEOUT) { // // Channel is idle - use the opportunity to send a // keepalive. Libssh2 will ignore the call if no // keepalive is due yet. // session.KeepAlive(); } else if (waitResult == UnsafeNativeMethods.WSA_WAIT_FAILED) { throw new Win32Exception( UnsafeNativeMethods.WSAGetLastError(), "WSAWaitForMultipleEvents failed"); } } catch (SshNativeException e) when(e.ErrorCode == LIBSSH2_ERROR.EAGAIN) { // Retry operation. } catch (Exception e) { SshTraceSources.Default.TraceError( "Socket I/O failed for {0}: {1}", Thread.CurrentThread.Name, e); if ((currentOperation & Operation.Sending) != 0) { this.OnSendError(e); } else { // Consider it a receive error. this.OnReceiveError(e); } // Bail out. return; } } // while }// nonblocking try { channel.Close(); } catch (Exception e) { // NB. This is non-fatal - we're tearing down the // connection anyway.. SshTraceSources.Default.TraceError( "Closing connection failed for {0}: {1}", Thread.CurrentThread.Name, e); } } } } catch (Exception e) { SshTraceSources.Default.TraceError( "Connection failed for {0}: {1}", Thread.CurrentThread.Name, e); OnConnectionError(e); } } }