| | | 1 | | using System; |
| | | 2 | | using System.Collections.Generic; |
| | | 3 | | using System.Linq; |
| | | 4 | | using System.Net.Sockets; |
| | | 5 | | using System.Text; |
| | | 6 | | |
| | | 7 | | using Renci.SshNet.Abstractions; |
| | | 8 | | using Renci.SshNet.Common; |
| | | 9 | | using Renci.SshNet.Connection; |
| | | 10 | | using Renci.SshNet.Messages.Transport; |
| | | 11 | | using Renci.SshNet.NetConf; |
| | | 12 | | using Renci.SshNet.Security; |
| | | 13 | | using Renci.SshNet.Sftp; |
| | | 14 | | |
| | | 15 | | namespace Renci.SshNet |
| | | 16 | | { |
| | | 17 | | /// <summary> |
| | | 18 | | /// Basic factory for creating new services. |
| | | 19 | | /// </summary> |
| | | 20 | | internal sealed partial class ServiceFactory : IServiceFactory |
| | | 21 | | { |
| | | 22 | | /// <summary> |
| | | 23 | | /// Defines the number of times an authentication attempt with any given <see cref="IAuthenticationMethod"/> |
| | | 24 | | /// can result in <see cref="AuthenticationResult.PartialSuccess"/> before it is disregarded. |
| | | 25 | | /// </summary> |
| | | 26 | | private const int PartialSuccessLimit = 5; |
| | | 27 | | |
| | | 28 | | /// <summary> |
| | | 29 | | /// Creates an <see cref="IClientAuthentication"/>. |
| | | 30 | | /// </summary> |
| | | 31 | | /// <returns> |
| | | 32 | | /// An <see cref="IClientAuthentication"/>. |
| | | 33 | | /// </returns> |
| | | 34 | | public IClientAuthentication CreateClientAuthentication() |
| | 1204 | 35 | | { |
| | 1204 | 36 | | return new ClientAuthentication(PartialSuccessLimit); |
| | 1204 | 37 | | } |
| | | 38 | | |
| | | 39 | | /// <summary> |
| | | 40 | | /// Creates a new <see cref="ISession"/> with the specified <see cref="ConnectionInfo"/> and |
| | | 41 | | /// <see cref="ISocketFactory"/>. |
| | | 42 | | /// </summary> |
| | | 43 | | /// <param name="connectionInfo">The <see cref="ConnectionInfo"/> to use for creating a new session.</param> |
| | | 44 | | /// <param name="socketFactory">A factory to create <see cref="Socket"/> instances.</param> |
| | | 45 | | /// <returns> |
| | | 46 | | /// An <see cref="ISession"/> for the specified <see cref="ConnectionInfo"/>. |
| | | 47 | | /// </returns> |
| | | 48 | | /// <exception cref="ArgumentNullException"><paramref name="connectionInfo"/> is <see langword="null"/>.</except |
| | | 49 | | /// <exception cref="ArgumentNullException"><paramref name="socketFactory"/> is <see langword="null"/>.</excepti |
| | | 50 | | public ISession CreateSession(ConnectionInfo connectionInfo, ISocketFactory socketFactory) |
| | 1211 | 51 | | { |
| | 1211 | 52 | | return new Session(connectionInfo, this, socketFactory); |
| | 1211 | 53 | | } |
| | | 54 | | |
| | | 55 | | /// <summary> |
| | | 56 | | /// Creates a new <see cref="ISftpSession"/> in a given <see cref="ISession"/> and with |
| | | 57 | | /// the specified operation timeout and encoding. |
| | | 58 | | /// </summary> |
| | | 59 | | /// <param name="session">The <see cref="ISession"/> to create the <see cref="ISftpSession"/> in.</param> |
| | | 60 | | /// <param name="operationTimeout">The number of milliseconds to wait for an operation to complete, or <c>-1</c> |
| | | 61 | | /// <param name="encoding">The encoding.</param> |
| | | 62 | | /// <param name="sftpMessageFactory">The factory to use for creating SFTP messages.</param> |
| | | 63 | | /// <returns> |
| | | 64 | | /// An <see cref="ISftpSession"/>. |
| | | 65 | | /// </returns> |
| | | 66 | | public ISftpSession CreateSftpSession(ISession session, int operationTimeout, Encoding encoding, ISftpResponseFa |
| | 618 | 67 | | { |
| | 618 | 68 | | return new SftpSession(session, operationTimeout, encoding, sftpMessageFactory); |
| | 618 | 69 | | } |
| | | 70 | | |
| | | 71 | | /// <summary> |
| | | 72 | | /// Create a new <see cref="PipeStream"/>. |
| | | 73 | | /// </summary> |
| | | 74 | | /// <returns> |
| | | 75 | | /// A <see cref="PipeStream"/>. |
| | | 76 | | /// </returns> |
| | | 77 | | public PipeStream CreatePipeStream() |
| | 359 | 78 | | { |
| | 359 | 79 | | return new PipeStream(); |
| | 359 | 80 | | } |
| | | 81 | | |
| | | 82 | | /// <summary> |
| | | 83 | | /// Negotiates a key exchange algorithm, and creates a <see cref="IKeyExchange" /> for the negotiated |
| | | 84 | | /// algorithm. |
| | | 85 | | /// </summary> |
| | | 86 | | /// <param name="clientAlgorithms">A <see cref="IDictionary{String, Type}"/> of the key exchange algorithms supp |
| | | 87 | | /// <param name="serverAlgorithms">The names of the key exchange algorithms supported by the SSH server.</param> |
| | | 88 | | /// <returns> |
| | | 89 | | /// A <see cref="IKeyExchange"/> that was negotiated between client and server. |
| | | 90 | | /// </returns> |
| | | 91 | | /// <exception cref="ArgumentNullException"><paramref name="clientAlgorithms"/> is <see langword="null"/>.</exce |
| | | 92 | | /// <exception cref="ArgumentNullException"><paramref name="serverAlgorithms"/> is <see langword="null"/>.</exce |
| | | 93 | | /// <exception cref="SshConnectionException">No key exchange algorithms are supported by both client and server. |
| | | 94 | | public IKeyExchange CreateKeyExchange(IDictionary<string, Type> clientAlgorithms, string[] serverAlgorithms) |
| | 1199 | 95 | | { |
| | 1199 | 96 | | if (clientAlgorithms is null) |
| | 0 | 97 | | { |
| | 0 | 98 | | throw new ArgumentNullException(nameof(clientAlgorithms)); |
| | | 99 | | } |
| | | 100 | | |
| | 1199 | 101 | | if (serverAlgorithms is null) |
| | 0 | 102 | | { |
| | 0 | 103 | | throw new ArgumentNullException(nameof(serverAlgorithms)); |
| | | 104 | | } |
| | | 105 | | |
| | | 106 | | // find an algorithm that is supported by both client and server |
| | 1199 | 107 | | var keyExchangeAlgorithmType = (from c in clientAlgorithms |
| | 3894 | 108 | | from s in serverAlgorithms |
| | 2530 | 109 | | where s == c.Key |
| | 2398 | 110 | | select c.Value).FirstOrDefault(); |
| | | 111 | | |
| | 1199 | 112 | | if (keyExchangeAlgorithmType is null) |
| | 0 | 113 | | { |
| | 0 | 114 | | throw new SshConnectionException("Failed to negotiate key exchange algorithm.", DisconnectReason.KeyExch |
| | | 115 | | } |
| | | 116 | | |
| | 1199 | 117 | | return keyExchangeAlgorithmType.CreateInstance<IKeyExchange>(); |
| | 1199 | 118 | | } |
| | | 119 | | |
| | | 120 | | /// <summary> |
| | | 121 | | /// Creates a new <see cref="INetConfSession"/> in a given <see cref="ISession"/> |
| | | 122 | | /// and with the specified operation timeout. |
| | | 123 | | /// </summary> |
| | | 124 | | /// <param name="session">The <see cref="ISession"/> to create the <see cref="INetConfSession"/> in.</param> |
| | | 125 | | /// <param name="operationTimeout">The number of milliseconds to wait for an operation to complete, or <c>-1</c> |
| | | 126 | | /// <returns> |
| | | 127 | | /// An <see cref="INetConfSession"/>. |
| | | 128 | | /// </returns> |
| | | 129 | | public INetConfSession CreateNetConfSession(ISession session, int operationTimeout) |
| | 0 | 130 | | { |
| | 0 | 131 | | return new NetConfSession(session, operationTimeout); |
| | 0 | 132 | | } |
| | | 133 | | |
| | | 134 | | /// <summary> |
| | | 135 | | /// Creates an <see cref="ISftpFileReader"/> for the specified file and with the specified |
| | | 136 | | /// buffer size. |
| | | 137 | | /// </summary> |
| | | 138 | | /// <param name="fileName">The file to read.</param> |
| | | 139 | | /// <param name="sftpSession">The SFTP session to use.</param> |
| | | 140 | | /// <param name="bufferSize">The size of buffer.</param> |
| | | 141 | | /// <returns> |
| | | 142 | | /// An <see cref="ISftpFileReader"/>. |
| | | 143 | | /// </returns> |
| | | 144 | | public ISftpFileReader CreateSftpFileReader(string fileName, ISftpSession sftpSession, uint bufferSize) |
| | 74 | 145 | | { |
| | | 146 | | const int defaultMaxPendingReads = 10; |
| | | 147 | | |
| | | 148 | | // Issue #292: Avoid overlapping SSH_FXP_OPEN and SSH_FXP_LSTAT requests for the same file as this |
| | | 149 | | // causes a performance degradation on Sun SSH |
| | 74 | 150 | | var openAsyncResult = sftpSession.BeginOpen(fileName, Flags.Read, callback: null, state: null); |
| | 74 | 151 | | var handle = sftpSession.EndOpen(openAsyncResult); |
| | | 152 | | |
| | 70 | 153 | | var statAsyncResult = sftpSession.BeginLStat(fileName, callback: null, state: null); |
| | | 154 | | |
| | | 155 | | long? fileSize; |
| | | 156 | | int maxPendingReads; |
| | | 157 | | |
| | 70 | 158 | | var chunkSize = sftpSession.CalculateOptimalReadLength(bufferSize); |
| | | 159 | | |
| | | 160 | | // fallback to a default maximum of pending reads when remote server does not allow us to obtain |
| | | 161 | | // the attributes of the file |
| | | 162 | | try |
| | 70 | 163 | | { |
| | 70 | 164 | | var fileAttributes = sftpSession.EndLStat(statAsyncResult); |
| | 67 | 165 | | fileSize = fileAttributes.Size; |
| | 67 | 166 | | maxPendingReads = Math.Min(100, (int)Math.Ceiling((double)fileAttributes.Size / chunkSize) + 1); |
| | 67 | 167 | | } |
| | 3 | 168 | | catch (SshException ex) |
| | 3 | 169 | | { |
| | 3 | 170 | | fileSize = null; |
| | 3 | 171 | | maxPendingReads = defaultMaxPendingReads; |
| | | 172 | | |
| | 3 | 173 | | DiagnosticAbstraction.Log(string.Format("Failed to obtain size of file. Allowing maximum {0} pending rea |
| | 3 | 174 | | } |
| | | 175 | | |
| | 70 | 176 | | return sftpSession.CreateFileReader(handle, sftpSession, chunkSize, maxPendingReads, fileSize); |
| | 70 | 177 | | } |
| | | 178 | | |
| | | 179 | | /// <summary> |
| | | 180 | | /// Creates a new <see cref="ISftpResponseFactory"/> instance. |
| | | 181 | | /// </summary> |
| | | 182 | | /// <returns> |
| | | 183 | | /// An <see cref="ISftpResponseFactory"/>. |
| | | 184 | | /// </returns> |
| | | 185 | | public ISftpResponseFactory CreateSftpResponseFactory() |
| | 618 | 186 | | { |
| | 618 | 187 | | return new SftpResponseFactory(); |
| | 618 | 188 | | } |
| | | 189 | | |
| | | 190 | | /// <summary> |
| | | 191 | | /// Creates a shell stream. |
| | | 192 | | /// </summary> |
| | | 193 | | /// <param name="session">The SSH session.</param> |
| | | 194 | | /// <param name="terminalName">The <c>TERM</c> environment variable.</param> |
| | | 195 | | /// <param name="columns">The terminal width in columns.</param> |
| | | 196 | | /// <param name="rows">The terminal width in rows.</param> |
| | | 197 | | /// <param name="width">The terminal width in pixels.</param> |
| | | 198 | | /// <param name="height">The terminal height in pixels.</param> |
| | | 199 | | /// <param name="terminalModeValues">The terminal mode values.</param> |
| | | 200 | | /// <param name="bufferSize">The size of the buffer.</param> |
| | | 201 | | /// <returns> |
| | | 202 | | /// The created <see cref="ShellStream"/> instance. |
| | | 203 | | /// </returns> |
| | | 204 | | /// <exception cref="SshConnectionException">Client is not connected.</exception> |
| | | 205 | | /// <remarks> |
| | | 206 | | /// <para> |
| | | 207 | | /// The <c>TERM</c> environment variable contains an identifier for the text window's capabilities. |
| | | 208 | | /// You can get a detailed list of these cababilities by using the ‘infocmp’ command. |
| | | 209 | | /// </para> |
| | | 210 | | /// <para> |
| | | 211 | | /// The column/row dimensions override the pixel dimensions(when non-zero). Pixel dimensions refer |
| | | 212 | | /// to the drawable area of the window. |
| | | 213 | | /// </para> |
| | | 214 | | /// </remarks> |
| | | 215 | | public ShellStream CreateShellStream(ISession session, string terminalName, uint columns, uint rows, uint width, |
| | 48 | 216 | | { |
| | 48 | 217 | | return new ShellStream(session, terminalName, columns, rows, width, height, terminalModeValues, bufferSize); |
| | 18 | 218 | | } |
| | | 219 | | |
| | | 220 | | /// <summary> |
| | | 221 | | /// Creates an <see cref="IRemotePathTransformation"/> that encloses a path in double quotes, and escapes |
| | | 222 | | /// any embedded double quote with a backslash. |
| | | 223 | | /// </summary> |
| | | 224 | | /// <returns> |
| | | 225 | | /// An <see cref="IRemotePathTransformation"/> that encloses a path in double quotes, and escapes any |
| | | 226 | | /// embedded double quote with a backslash. |
| | | 227 | | /// with a shell. |
| | | 228 | | /// </returns> |
| | | 229 | | public IRemotePathTransformation CreateRemotePathDoubleQuoteTransformation() |
| | 309 | 230 | | { |
| | 309 | 231 | | return RemotePathTransformation.DoubleQuote; |
| | 309 | 232 | | } |
| | | 233 | | |
| | | 234 | | /// <summary> |
| | | 235 | | /// Creates an <see cref="IConnector"/> that can be used to establish a connection |
| | | 236 | | /// to the server identified by the specified <paramref name="connectionInfo"/>. |
| | | 237 | | /// </summary> |
| | | 238 | | /// <param name="connectionInfo">A <see cref="IConnectionInfo"/> detailing the server to establish a connection |
| | | 239 | | /// <param name="socketFactory">A factory to create <see cref="Socket"/> instances.</param> |
| | | 240 | | /// <returns> |
| | | 241 | | /// An <see cref="IConnector"/> that can be used to establish a connection to the |
| | | 242 | | /// server identified by the specified <paramref name="connectionInfo"/>. |
| | | 243 | | /// </returns> |
| | | 244 | | /// <exception cref="ArgumentNullException"><paramref name="connectionInfo"/> is <see langword="null"/>.</except |
| | | 245 | | /// <exception cref="ArgumentNullException"><paramref name="socketFactory"/> is <see langword="null"/>.</excepti |
| | | 246 | | /// <exception cref="NotSupportedException">The <see cref="IConnectionInfo.ProxyType"/> value of <paramref name= |
| | | 247 | | public IConnector CreateConnector(IConnectionInfo connectionInfo, ISocketFactory socketFactory) |
| | 1232 | 248 | | { |
| | 1232 | 249 | | if (connectionInfo is null) |
| | 3 | 250 | | { |
| | 3 | 251 | | throw new ArgumentNullException(nameof(connectionInfo)); |
| | | 252 | | } |
| | | 253 | | |
| | 1229 | 254 | | if (socketFactory is null) |
| | 3 | 255 | | { |
| | 3 | 256 | | throw new ArgumentNullException(nameof(socketFactory)); |
| | | 257 | | } |
| | | 258 | | |
| | 1226 | 259 | | switch (connectionInfo.ProxyType) |
| | | 260 | | { |
| | | 261 | | case ProxyTypes.None: |
| | 1208 | 262 | | return new DirectConnector(socketFactory); |
| | | 263 | | case ProxyTypes.Socks4: |
| | 3 | 264 | | return new Socks4Connector(socketFactory); |
| | | 265 | | case ProxyTypes.Socks5: |
| | 3 | 266 | | return new Socks5Connector(socketFactory); |
| | | 267 | | case ProxyTypes.Http: |
| | 9 | 268 | | return new HttpConnector(socketFactory); |
| | | 269 | | default: |
| | 3 | 270 | | throw new NotSupportedException(string.Format("ProxyTypes '{0}' is not supported.", connectionInfo.P |
| | | 271 | | } |
| | 1223 | 272 | | } |
| | | 273 | | |
| | | 274 | | /// <summary> |
| | | 275 | | /// Creates an <see cref="IProtocolVersionExchange"/> that deals with the SSH protocol |
| | | 276 | | /// version exchange. |
| | | 277 | | /// </summary> |
| | | 278 | | /// <returns> |
| | | 279 | | /// An <see cref="IProtocolVersionExchange"/>. |
| | | 280 | | /// </returns> |
| | | 281 | | public IProtocolVersionExchange CreateProtocolVersionExchange() |
| | 1199 | 282 | | { |
| | 1199 | 283 | | return new ProtocolVersionExchange(); |
| | 1199 | 284 | | } |
| | | 285 | | |
| | | 286 | | /// <summary> |
| | | 287 | | /// Creates a factory to create <see cref="Socket"/> instances. |
| | | 288 | | /// </summary> |
| | | 289 | | /// <returns> |
| | | 290 | | /// An <see cref="ISocketFactory"/>. |
| | | 291 | | /// </returns> |
| | | 292 | | public ISocketFactory CreateSocketFactory() |
| | 1211 | 293 | | { |
| | 1211 | 294 | | return new SocketFactory(); |
| | 1211 | 295 | | } |
| | | 296 | | } |
| | | 297 | | } |