using System; using System.Collections.Generic; using System.Linq; using System.Net.Sockets; using System.Text; using Microsoft.Extensions.Logging; using Renci.SshNet.Common; using Renci.SshNet.Connection; using Renci.SshNet.Messages.Transport; using Renci.SshNet.NetConf; 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 const int PartialSuccessLimit = 5; internal ServiceFactory() { } /// /// Creates an . /// /// /// An . /// 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 . /// is . 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(); } /// public IKeyExchange CreateKeyExchange(IDictionary> clientAlgorithms, string[] serverAlgorithms) { ThrowHelper.ThrowIfNull(clientAlgorithms); ThrowHelper.ThrowIfNull(serverAlgorithms); // find an algorithm that is supported by both client and server var keyExchangeAlgorithmFactory = (from c in clientAlgorithms from s in serverAlgorithms where s == c.Key select c.Value).FirstOrDefault(); if (keyExchangeAlgorithmFactory is null) { throw new SshConnectionException($"No matching key exchange algorithm (server offers {serverAlgorithms.Join(",")})", DisconnectReason.KeyExchangeFailed); } return keyExchangeAlgorithmFactory(); } /// /// Creates a new in a given /// and with the specified operation timeout. /// /// The to create the in. /// The number of milliseconds to wait for an operation to complete, or -1 to wait indefinitely. /// /// An . /// public INetConfSession CreateNetConfSession(ISession session, int operationTimeout) { return new NetConfSession(session, operationTimeout); } /// /// Creates an for the specified file and with the specified /// buffer size. /// /// The file to read. /// The SFTP session to use. /// The size of buffer. /// /// An . /// public ISftpFileReader CreateSftpFileReader(string fileName, ISftpSession sftpSession, uint bufferSize) { const int defaultMaxPendingReads = 10; // 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(100, (int)Math.Ceiling((double)fileAttributes.Size / chunkSize) + 1); } catch (SshException ex) { fileSize = null; maxPendingReads = defaultMaxPendingReads; sftpSession.SessionLoggerFactory.CreateLogger().LogInformation(ex, "Failed to obtain size of file. Allowing maximum {MaxPendingReads} pending reads", maxPendingReads); } return sftpSession.CreateFileReader(handle, sftpSession, chunkSize, maxPendingReads, fileSize); } /// /// Creates a new instance. /// /// /// An . /// public ISftpResponseFactory CreateSftpResponseFactory() { return new SftpResponseFactory(); } /// 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); } /// public ShellStream CreateShellStreamNoTerminal(ISession session, int bufferSize) { return new ShellStream(session, 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) { ThrowHelper.ThrowIfNull(connectionInfo); ThrowHelper.ThrowIfNull(socketFactory); var loggerFactory = connectionInfo.LoggerFactory ?? SshNetLoggingConfiguration.LoggerFactory; switch (connectionInfo.ProxyType) { case ProxyTypes.None: return new DirectConnector(socketFactory, loggerFactory); case ProxyTypes.Socks4: return new Socks4Connector(socketFactory, loggerFactory); case ProxyTypes.Socks5: return new Socks5Connector(socketFactory, loggerFactory); case ProxyTypes.Http: return new HttpConnector(socketFactory, loggerFactory); 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(); } } }