public IObservable <IEnumerable <desktop> > GetHostListAsync()
        {
            bool doConnect = !_session.IsConnected;

            if (doConnect)
            {
                _session.Connect();
            }
            try
            {
                _session.AddPortForwarding(_localPortForwarding);
                return(_http.GetHostListAsync());
            }
            catch (Exception)
            {
                throw;
            }

            finally
            {
                _session.RemovePortForwarding(_localPortForwarding);
                if (doConnect)
                {
                    _session.Disconnect();
                }
            }
        }
Esempio n. 2
0
        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);
                }
            }
        }