/// <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); } } }
/// <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)); } } } } }
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)); }
/// <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); }
/// <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); }
/// <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."); } }
/// <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); }
/// <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); } } } }
/// <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(); }
/// <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); } }
/// <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)); }
/// <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; } }