sources/Google.Solutions.Ssh/Native/Libssh2ChannelBase.cs (156 lines of code) (raw):

// // Copyright 2020 Google LLC // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // using Google.Solutions.Common.Diagnostics; using Google.Solutions.Common.Util; using System; using System.Diagnostics; namespace Google.Solutions.Ssh.Native { /// <summary> /// An connected and authenticated Libssh2 session. /// </summary> internal abstract class Libssh2ChannelBase : IDisposable { // NB. This object does not own this handle and should not dispose it. protected Libssh2Session Session { get; } internal Libssh2ChannelHandle ChannelHandle { get; } private bool closedForWriting = false; private bool disposed = false; //--------------------------------------------------------------------- // Ctor. //--------------------------------------------------------------------- internal Libssh2ChannelBase( Libssh2Session session, Libssh2ChannelHandle channelHandle) { this.Session = session; this.ChannelHandle = channelHandle; } //--------------------------------------------------------------------- // I/O. //--------------------------------------------------------------------- /// <summary> /// Indicates whether the server has sent an EOF. /// </summary> public bool IsEndOfStream { get { this.ChannelHandle.CheckCurrentThreadOwnsHandle(); using (SshTraceSource.Log.TraceMethod().WithoutParameters()) { return NativeMethods.libssh2_channel_eof( this.ChannelHandle) == 1; } } } public virtual uint Read( byte[] buffer, LIBSSH2_STREAM streamId = LIBSSH2_STREAM.NORMAL) { this.ChannelHandle.CheckCurrentThreadOwnsHandle(); Precondition.ExpectNotNull(buffer, nameof(buffer)); using (SshTraceSource.Log.TraceMethod().WithParameters(streamId)) { if (this.IsEndOfStream) { // Server sent EOF, trying to read would just // end up in a timeout. return 0u; } if (SshEventSource.Log.IsEnabled()) { SshEventSource.Log.ChannelReadInitiated(buffer.Length); } var bytesRead = NativeMethods.libssh2_channel_read_ex( this.ChannelHandle, (int)streamId, buffer, new IntPtr(buffer.Length)); if (SshEventSource.Log.IsEnabled()) { SshEventSource.Log.ChannelReadCompleted(bytesRead); } if (bytesRead >= 0) { return (uint)bytesRead; } else if (((LIBSSH2_ERROR)bytesRead) == LIBSSH2_ERROR.EAGAIN) { return 0; } else { throw this.Session.CreateException((LIBSSH2_ERROR)bytesRead); } } } public virtual uint Write( byte[] buffer, LIBSSH2_STREAM streamId = LIBSSH2_STREAM.NORMAL) { this.ChannelHandle.CheckCurrentThreadOwnsHandle(); Precondition.ExpectNotNull(buffer, nameof(buffer)); using (SshTraceSource.Log.TraceMethod().WithParameters(streamId)) { Debug.Assert(!this.closedForWriting); if (SshEventSource.Log.IsEnabled()) { SshEventSource.Log.ChannelWriteInitiated(buffer.Length); } var bytesWritten = NativeMethods.libssh2_channel_write_ex( this.ChannelHandle, (int)streamId, buffer, new IntPtr(buffer.Length)); if (SshEventSource.Log.IsEnabled()) { SshEventSource.Log.ChannelWriteCompleted(bytesWritten); } if (bytesWritten >= 0) { return (uint)bytesWritten; } else { throw this.Session.CreateException((LIBSSH2_ERROR)bytesWritten); } } } public void WaitForEndOfStream() { this.ChannelHandle.CheckCurrentThreadOwnsHandle(); using (SshTraceSource.Log.TraceMethod().WithoutParameters()) { var result = (LIBSSH2_ERROR)NativeMethods.libssh2_channel_wait_eof( this.ChannelHandle); if (result != LIBSSH2_ERROR.NONE) { throw this.Session.CreateException((LIBSSH2_ERROR)result); } } } public void Close() { this.ChannelHandle.CheckCurrentThreadOwnsHandle(); using (SshTraceSource.Log.TraceMethod().WithoutParameters()) { // Avoid closing more than once. if (!this.closedForWriting) { SshEventSource.Log.ChannelCloseInitiated(); var result = (LIBSSH2_ERROR)NativeMethods.libssh2_channel_close( this.ChannelHandle); if (result == LIBSSH2_ERROR.SOCKET_SEND) { // Broken connection, nevermind then. } else if (result != LIBSSH2_ERROR.NONE) { throw this.Session.CreateException((LIBSSH2_ERROR)result); } this.closedForWriting = true; } } } //--------------------------------------------------------------------- // IDisposable. //--------------------------------------------------------------------- public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (this.disposed) { return; } if (disposing) { this.ChannelHandle.Dispose(); this.disposed = true; } } } }