using System; using System.Collections.Generic; using System.Linq; using System.Net.Sockets; using System.Text; using Renci.SshNet.Abstractions; using Renci.SshNet.Common; using Renci.SshNet.Connection; using Renci.SshNet.Messages.Transport; using Renci.SshNet.Security; using Renci.SshNet.Sftp; namespace Renci.SshNet { /// /// Basic factory for creating new services. /// internal sealed partial class ServiceFactory : IServiceFactory { /// /// Defines the number of times an authentication attempt with any given /// can result in before it is disregarded. /// private static readonly int PartialSuccessLimit = 5; /// /// Creates a . /// /// /// A . /// public IClientAuthentication CreateClientAuthentication() { return new ClientAuthentication(PartialSuccessLimit); } /// /// Creates a new with the specified and /// . /// /// The to use for creating a new session. /// A factory to create instances. /// /// An for the specified . /// /// is null. /// is null. public ISession CreateSession(ConnectionInfo connectionInfo, ISocketFactory socketFactory) { return new Session(connectionInfo, this, socketFactory); } /// /// Creates a new in a given and with /// the specified operation timeout and encoding. /// /// The to create the in. /// The number of milliseconds to wait for an operation to complete, or -1 to wait indefinitely. /// The encoding. /// The factory to use for creating SFTP messages. /// /// An . /// public ISftpSession CreateSftpSession(ISession session, int operationTimeout, Encoding encoding, ISftpResponseFactory sftpMessageFactory) { return new SftpSession(session, operationTimeout, encoding, sftpMessageFactory); } /// /// Create a new . /// /// /// A . /// public PipeStream CreatePipeStream() { return new PipeStream(); } /// /// Negotiates a key exchange algorithm, and creates a for the negotiated /// algorithm. /// /// A of the key exchange algorithms supported by the client where key is the name of the algorithm, and value is the type implementing this algorithm. /// The names of the key exchange algorithms supported by the SSH server. /// /// A that was negotiated between client and server. /// /// is null. /// is null. /// No key exchange algorithms are supported by both client and server. public IKeyExchange CreateKeyExchange(IDictionary clientAlgorithms, string[] serverAlgorithms) { if (clientAlgorithms is null) { throw new ArgumentNullException(nameof(clientAlgorithms)); } if (serverAlgorithms is null) { throw new ArgumentNullException(nameof(serverAlgorithms)); } // find an algorithm that is supported by both client and server var keyExchangeAlgorithmType = (from c in clientAlgorithms from s in serverAlgorithms where s == c.Key select c.Value).FirstOrDefault(); if (keyExchangeAlgorithmType is null) { throw new SshConnectionException("Failed to negotiate key exchange algorithm.", DisconnectReason.KeyExchangeFailed); } return keyExchangeAlgorithmType.CreateInstance(); } 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, callback: null, state: null); var handle = sftpSession.EndOpen(openAsyncResult); var statAsyncResult = sftpSession.BeginLStat(fileName, callback: null, state: 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); } public ISftpResponseFactory CreateSftpResponseFactory() { return new SftpResponseFactory(); } /// /// Creates a shell stream. /// /// The SSH session. /// The TERM environment variable. /// The terminal width in columns. /// The terminal width in rows. /// The terminal width in pixels. /// The terminal height in pixels. /// The terminal mode values. /// The size of the buffer. /// /// The created instance. /// /// Client is not connected. /// /// /// The TERM environment variable contains an identifier for the text window's capabilities. /// You can get a detailed list of these cababilities by using the ‘infocmp’ command. /// /// /// The column/row dimensions override the pixel dimensions(when non-zero). Pixel dimensions refer /// to the drawable area of the window. /// /// public ShellStream CreateShellStream(ISession session, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary terminalModeValues, int bufferSize) { return new ShellStream(session, terminalName, columns, rows, width, height, terminalModeValues, bufferSize); } /// /// Creates an that encloses a path in double quotes, and escapes /// any embedded double quote with a backslash. /// /// /// An that encloses a path in double quotes, and escapes any /// embedded double quote with a backslash. /// with a shell. /// public IRemotePathTransformation CreateRemotePathDoubleQuoteTransformation() { return RemotePathTransformation.DoubleQuote; } /// /// Creates an that can be used to establish a connection /// to the server identified by the specified . /// /// A detailing the server to establish a connection to. /// A factory to create instances. /// /// An that can be used to establish a connection to the /// server identified by the specified . /// /// is . /// is . /// The value of is not supported. public IConnector CreateConnector(IConnectionInfo connectionInfo, ISocketFactory socketFactory) { if (connectionInfo is null) { throw new ArgumentNullException(nameof(connectionInfo)); } if (socketFactory is null) { throw new ArgumentNullException(nameof(socketFactory)); } switch (connectionInfo.ProxyType) { case ProxyTypes.None: return new DirectConnector(socketFactory); case ProxyTypes.Socks4: return new Socks4Connector(socketFactory); case ProxyTypes.Socks5: return new Socks5Connector(socketFactory); case ProxyTypes.Http: return new HttpConnector(socketFactory); default: throw new NotSupportedException(string.Format("ProxyTypes '{0}' is not supported.", connectionInfo.ProxyType)); } } /// /// Creates an that deals with the SSH protocol /// version exchange. /// /// /// An . /// public IProtocolVersionExchange CreateProtocolVersionExchange() { return new ProtocolVersionExchange(); } /// /// Creates a factory to create instances. /// /// /// An . /// public ISocketFactory CreateSocketFactory() { return new SocketFactory(); } } }