示例#1
0
        /// <summary>
        /// Shuts down the socket.
        /// </summary>
        /// <param name="how">One of the <see cref="SocketShutdown"/> values that specifies the operation that will no longer be allowed.</param>
        private void ShutdownSocket(SocketShutdown how)
        {
            if (_socket == null)
            {
                return;
            }

            lock (_socketLock)
            {
                if (!_socket.IsConnected())
                {
                    return;
                }

                try
                {
                    _socket.Shutdown(how);
                }
                catch (SocketException ex)
                {
                    // TODO: log as warning
                    DiagnosticAbstraction.Log("Failure shutting down socket: " + ex);
                }
            }
        }
示例#2
0
        /// <summary>
        /// Closes the channel, waiting for the SSH_MSG_CHANNEL_CLOSE message to be received from the server.
        /// </summary>
        protected virtual void Close()
        {
            // synchronize sending SSH_MSG_CHANNEL_EOF and SSH_MSG_CHANNEL_CLOSE to ensure that these messages
            // are sent in that other; when both the client and the server attempt to close the channel at the
            // same time we would otherwise risk sending the SSH_MSG_CHANNEL_EOF after the SSH_MSG_CHANNEL_CLOSE
            // message causing the server to disconnect the session.

            lock (this)
            {
                // send EOF message first the following conditions are met:
                // * we have not sent a SSH_MSG_CHANNEL_EOF message
                // * remote party has not already sent a SSH_MSG_CHANNEL_EOF message
                // * remote party has not already sent a SSH_MSG_CHANNEL_CLOSE message
                // * the channel is open
                // * the session is connected
                if (!_eofMessageSent && !_closeMessageReceived && !_eofMessageReceived && IsOpen && IsConnected)
                {
                    if (TrySendMessage(new ChannelEofMessage(RemoteChannelNumber)))
                    {
                        _eofMessageSent = true;
                    }
                }

                // send message to close the channel on the server when it has not already been sent
                // and the channel is open and the session is connected
                if (!_closeMessageSent && IsOpen && IsConnected)
                {
                    if (TrySendMessage(new ChannelCloseMessage(RemoteChannelNumber)))
                    {
                        _closeMessageSent = true;

                        // only wait for the channel to be closed by the server if we didn't send a
                        // SSH_MSG_CHANNEL_CLOSE as response to a SSH_MSG_CHANNEL_CLOSE sent by the
                        // server
                        var closeWaitResult = _session.TryWait(_channelClosedWaitHandle, ConnectionInfo.ChannelCloseTimeout);
                        if (closeWaitResult != WaitResult.Success)
                        {
                            DiagnosticAbstraction.Log(string.Format("Wait for channel close not successful: {0:G}.", closeWaitResult));
                        }
                    }
                }

                if (IsOpen)
                {
                    // mark sure the channel is marked closed before we raise the Closed event
                    // this also ensures don't raise the Closed event more than once
                    IsOpen = false;

                    if (_closeMessageReceived)
                    {
                        // raise event signaling that both ends of the channel have been closed
                        var closed = Closed;
                        if (closed != null)
                        {
                            closed(this, new ChannelEventArgs(LocalChannelNumber));
                        }
                    }
                }
            }
        }
示例#3
0
        public ISftpFileReader CreateSftpFileReader(string fileName, ISftpSession sftpSession, uint bufferSize)
        {
            const int defaultMaxPendingReads = 3;

            // Issue #292: Avoid overlapping SSH_FXP_OPEN and SSH_FXP_LSTAT requests for the same file as this
            // causes a performance degradation on Sun SSH
            var openAsyncResult = sftpSession.BeginOpen(fileName, Flags.Read, null, null);
            var handle          = sftpSession.EndOpen(openAsyncResult);

            var statAsyncResult = sftpSession.BeginLStat(fileName, null, null);

            long?fileSize;
            int  maxPendingReads;

            var chunkSize = sftpSession.CalculateOptimalReadLength(bufferSize);

            // fallback to a default maximum of pending reads when remote server does not allow us to obtain
            // the attributes of the file
            try
            {
                var fileAttributes = sftpSession.EndLStat(statAsyncResult);
                fileSize        = fileAttributes.Size;
                maxPendingReads = Math.Min(10, (int)Math.Ceiling((double)fileAttributes.Size / chunkSize) + 1);
            }
            catch (SshException ex)
            {
                fileSize        = null;
                maxPendingReads = defaultMaxPendingReads;

                DiagnosticAbstraction.Log(string.Format("Failed to obtain size of file. Allowing maximum {0} pending reads: {1}", maxPendingReads, ex));
            }

            return(sftpSession.CreateFileReader(handle, sftpSession, chunkSize, maxPendingReads, fileSize));
        }
示例#4
0
        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            DiagnosticAbstraction.Log("Disposing client.");

            Dispose(true);
            GC.SuppressFinalize(this);
        }
示例#5
0
        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            DiagnosticAbstraction.Log(string.Format("{0} Disposing client", DateTime.Now.Ticks));

            Dispose(true);
            GC.SuppressFinalize(this);
        }
示例#6
0
        /// <summary>
        /// Disconnects client from the server.
        /// </summary>
        /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
        public void Disconnect()
        {
            DiagnosticAbstraction.Log(string.Format("{0} Disconnecting client", DateTime.Now.Ticks));

            CheckDisposed();

            OnDisconnecting();

            // stop sending keep-alive messages before we close the
            // session
            StopKeepAliveTimer();

            // disconnect and dispose the SSH session
            if (Session != null)
            {
                // a new session is created in Connect(), so we should dispose and
                // dereference the current session here
                Session.ErrorOccured    -= Session_ErrorOccured;
                Session.HostKeyReceived -= Session_HostKeyReceived;
                Session.Dispose();
                Session = null;
            }

            OnDisconnected();
        }
        /// <summary>
        /// Stops remote port forwarding.
        /// </summary>
        /// <param name="timeout">The maximum amount of time to wait for the port to stop.</param>
        protected override void StopPort(TimeSpan timeout)
        {
            if (!ForwardedPortStatus.ToStopping(ref _status))
            {
                return;
            }

            base.StopPort(timeout);

            // send global request to cancel direct tcpip
            Session.SendMessage(new CancelTcpIpForwardGlobalRequestMessage(BoundHost, BoundPort));
            // wait for response on global request to cancel direct tcpip or completion of message
            // listener loop (in which case response on global request can never be received)
            WaitHandle.WaitAny(new[] { _globalRequestResponse, Session.MessageListenerCompleted }, timeout);

            // unsubscribe from session events as either the tcpip forward is cancelled at the
            // server, or our session message loop has completed
            Session.RequestSuccessReceived -= Session_RequestSuccess;
            Session.RequestFailureReceived -= Session_RequestFailure;
            Session.ChannelOpenReceived    -= Session_ChannelOpening;

            // wait for pending channels to close
            _pendingChannelCountdown.Signal();

            if (!_pendingChannelCountdown.Wait(timeout))
            {
                // TODO: log as warning
                DiagnosticAbstraction.Log("Timeout waiting for pending channels in remote forwarded port to close.");
            }

            _status = ForwardedPortStatus.Stopped;
        }
 /// <summary>
 /// Waits for pending channels to close.
 /// </summary>
 /// <param name="timeout">The maximum time to wait for the pending channels to close.</param>
 partial void InternalStop(TimeSpan timeout)
 {
     _pendingChannelCountdown.Signal();
     if (!_pendingChannelCountdown.Wait(timeout))
     {
         // TODO: log as warning
         DiagnosticAbstraction.Log("Timeout waiting for pending channels in dynamic forwarded port to close.");
     }
 }
示例#9
0
        /// <summary>
        /// Raises the error.
        /// </summary>
        /// <param name="error">The error.</param>
        protected void RaiseError(Exception error)
        {
            _exception = error;

            DiagnosticAbstraction.Log("Raised exception: " + error);

            var errorOccuredWaitHandle = _errorOccuredWaitHandle;

            if (errorOccuredWaitHandle != null)
            {
                errorOccuredWaitHandle.Set();
            }

            SignalErrorOccurred(error);
        }
示例#10
0
        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        protected void Dispose(bool disposing)
        {
            if (_disposingOrDisposed)
            {
                return;
            }

            // transition to disposing state
            _disposingOrDisposed = true;

            if (disposing)
            {
                // record exception to break prevent future Read()
                _exception = new ObjectDisposedException(GetType().FullName);

                // signal that we're disposing to interrupt wait in read-ahead
                _disposingWaitHandle.Set();

                // wait until the read-ahead thread has completed
                _readAheadCompleted.WaitOne();

                // unblock the Read()
                lock (_readLock)
                {
                    // dispose semaphore in read lock to ensure we don't run into an ObjectDisposedException
                    // in Read()
                    _semaphore.Dispose();
                    // awake Read
                    Monitor.PulseAll(_readLock);
                }

                _readAheadCompleted.Dispose();
                _disposingWaitHandle.Dispose();

                if (_sftpSession.IsOpen)
                {
                    try
                    {
                        var closeAsyncResult = _sftpSession.BeginClose(_handle, null, null);
                        _sftpSession.EndClose(closeAsyncResult);
                    }
                    catch (Exception ex)
                    {
                        DiagnosticAbstraction.Log("Failure closing handle: " + ex);
                    }
                }
            }
        }
示例#11
0
        /// <summary>
        /// Disconnects client from the server.
        /// </summary>
        /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
        public void Disconnect()
        {
            DiagnosticAbstraction.Log("Disconnecting client.");

            CheckDisposed();

            OnDisconnecting();

            // stop sending keep-alive messages before we close the session
            StopKeepAliveTimer();

            // dispose the SSH session
            DisposeSession();

            OnDisconnected();
        }
示例#12
0
        /// <summary>
        /// Called when type specific data need to be loaded.
        /// </summary>
        protected override void LoadData()
        {
            var dataLength = ReadUInt32();

            if (dataLength > int.MaxValue)
            {
                throw new NotSupportedException(string.Format(CultureInfo.CurrentCulture, "Data longer than {0} is not supported.", int.MaxValue));
            }

            if (dataLength > (DataStream.Length - DataStream.Position))
            {
                DiagnosticAbstraction.Log("SSH_MSG_IGNORE: Length exceeds data bytes, data ignored.");
                Data = Array <byte> .Empty;
            }
            else
            {
                Data = ReadBytes((int)dataLength);
            }
        }
示例#13
0
        /// <summary>
        /// Creates the server side cipher to use.
        /// </summary>
        /// <returns>Server cipher.</returns>
        public Cipher CreateServerCipher()
        {
            //  Resolve Session ID
            var sessionId = Session.SessionId ?? ExchangeHash;

            //  Calculate server to client initial IV
            var serverVector = Hash(GenerateSessionKey(SharedKey, ExchangeHash, 'B', sessionId));

            //  Calculate server to client encryption
            var serverKey = Hash(GenerateSessionKey(SharedKey, ExchangeHash, 'D', sessionId));

            serverKey = GenerateSessionKey(SharedKey, ExchangeHash, serverKey, _serverCipherInfo.KeySize / 8);

            DiagnosticAbstraction.Log(string.Format("[{0}] Creating server cipher (Name:{1},Key:{2},IV:{3})",
                                                    Session.ToHex(Session.SessionId),
                                                    Session.ConnectionInfo.CurrentServerEncryption,
                                                    Session.ToHex(serverKey),
                                                    Session.ToHex(serverVector)));

            //  Create server cipher
            return(_serverCipherInfo.Cipher(serverKey, serverVector));
        }
示例#14
0
        /// <summary>
        /// Establishes a socket connection to the specified host and port.
        /// </summary>
        /// <param name="host">The host name of the server to connect to.</param>
        /// <param name="port">The port to connect to.</param>
        /// <param name="timeout">The maximum time to wait for the connection to be established.</param>
        /// <exception cref="SshOperationTimeoutException">The connection failed to establish within the configured <see cref="ConnectionInfo.Timeout"/>.</exception>
        /// <exception cref="SocketException">An error occurred trying to establish the connection.</exception>
        protected Socket SocketConnect(string host, int port, TimeSpan timeout)
        {
            var ipAddress = DnsAbstraction.GetHostAddresses(host)[0];
            var ep        = new IPEndPoint(ipAddress, port);

            DiagnosticAbstraction.Log(string.Format("Initiating connection to '{0}:{1}'.", host, port));

            var socket = SocketFactory.Create(ep.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

            try
            {
                SocketAbstraction.Connect(socket, ep, timeout);

                const int socketBufferSize = 2 * Session.MaximumSshPacketSize;
                socket.SendBufferSize    = socketBufferSize;
                socket.ReceiveBufferSize = socketBufferSize;
                return(socket);
            }
            catch (Exception)
            {
                socket.Dispose();
                throw;
            }
        }