/// <summary> /// Automatic FTP and FTPS connection negotiation. /// This method tries every possible combination of the FTP connection properties, and returns the list of successful connection profiles. /// You can configure it to stop after finding the first successful profile, or to collect all successful profiles. /// You can then generate code for the profile using the FtpProfile.ToCode method. /// If no successful profiles are found, a blank list is returned. /// </summary> /// <param name="firstOnly">Find all successful profiles (false) or stop after finding the first successful profile (true)</param> /// <param name="cloneConnection">Use a new cloned FtpClient for testing connection profiles (true) or use the source FtpClient (false)</param> /// <returns></returns> public List <FtpProfile> AutoDetect(bool firstOnly = true, bool cloneConnection = true) { var results = new List <FtpProfile>(); #if !CORE14 lock (m_lock) { #endif LogFunc(nameof(AutoDetect), new object[] { firstOnly, cloneConnection }); ValidateAutoDetect(); // get known working connection profile based on the host (if any) var knownProfile = FtpServerSpecificHandler.GetWorkingProfileFromHost(Host, Port); if (knownProfile != null) { results.Add(knownProfile); return(results); } var blacklistedEncryptions = new List <FtpEncryptionMode>(); bool resetPort = m_port == 0; // clone this connection or use this connection var conn = cloneConnection ? CloneConnection() : this; // copy basic props if cloned connection if (cloneConnection) { conn.Host = this.Host; conn.Port = this.Port; conn.Credentials = this.Credentials; } // disconnect if already connected if (conn.IsConnected) { conn.Disconnect(); } // try each encryption mode foreach (var encryption in autoConnectEncryption) { // skip if FTPS was tried and failed if (blacklistedEncryptions.Contains(encryption)) { continue; } // try each SSL protocol foreach (var protocol in autoConnectProtocols) { // skip plain protocols if testing secure FTPS -- disabled because 'None' is recommended by Microsoft /*if (encryption != FtpEncryptionMode.None && protocol == SysSslProtocols.None) { * continue; * }*/ // skip secure protocols if testing plain FTP if (encryption == FtpEncryptionMode.None && protocol != SysSslProtocols.None) { continue; } // reset port so it auto computes based on encryption type if (resetPort) { conn.Port = 0; } // set rolled props conn.EncryptionMode = encryption; conn.SslProtocols = protocol; conn.DataConnectionType = FtpDataConnectionType.AutoPassive; conn.Encoding = Encoding.UTF8; // try to connect var connected = false; var dataConn = FtpDataConnectionType.PASV; try { conn.Connect(); connected = true; // get data connection once connected dataConn = AutoDataConnection(conn); // if non-cloned connection, we want to remain connected if it works if (cloneConnection) { conn.Disconnect(); } } catch (Exception ex) { #if !CORE14 if (ex is AuthenticationException) { throw new FtpInvalidCertificateException(); } #endif // since the connection failed, disconnect and retry conn.Disconnect(); // if server does not support FTPS no point trying encryption again if (IsFtpsFailure(blacklistedEncryptions, encryption, ex)) { goto SkipEncryptionMode; } // catch error "no such host is known" and hard abort if (IsPermanantConnectionFailure(ex)) { if (cloneConnection) { conn.Dispose(); } // rethrow permanent failures so caller can be made aware of it throw; } } // if it worked if (connected) { // if connected by explicit FTPS failed, no point trying encryption again if (IsConnectedButFtpsFailure(blacklistedEncryptions, encryption, conn._ConnectionFTPSFailure)) { } results.Add(new FtpProfile { Host = Host, Credentials = Credentials, Encryption = blacklistedEncryptions.Contains(encryption) ? FtpEncryptionMode.None : encryption, Protocols = protocol, DataConnection = dataConn, Encoding = Encoding.UTF8, EncodingVerified = conn._ConnectionUTF8Success || conn.HasFeature(FtpCapability.UTF8) }); // stop if only 1 wanted if (firstOnly) { goto Exit; } } } SkipEncryptionMode: var skip = true; } Exit: if (cloneConnection) { conn.Dispose(); } #if !CORE14 } #endif return(results); }
/// <summary> /// Automatic FTP and FTPS connection negotiation. /// This method tries every possible combination of the FTP connection properties, and returns the list of successful connection profiles. /// You can configure it to stop after finding the first successful profile, or to collect all successful profiles. /// You can then generate code for the profile using the FtpProfile.ToCode method. /// If no successful profiles are found, a blank list is returned. /// </summary> /// <param name="firstOnly">Find all successful profiles (false) or stop after finding the first successful profile (true)?</param> /// <returns></returns> public List <FtpProfile> AutoDetect(bool firstOnly = true) { var results = new List <FtpProfile>(); #if !CORE14 lock (m_lock) { #endif LogFunc(nameof(AutoDetect), new object[] { firstOnly }); if (IsDisposed) { throw new ObjectDisposedException("This FtpClient object has been disposed. It is no longer accessible."); } if (Host == null) { throw new FtpException("No host has been specified. Please set the 'Host' property before trying to auto connect."); } if (Credentials == null) { throw new FtpException("No username and password has been specified. Please set the 'Credentials' property before trying to auto connect."); } // get known working connection profile based on the host (if any) var knownProfile = FtpServerSpecificHandler.GetWorkingProfileFromHost(Host, Port); if (knownProfile != null) { results.Add(knownProfile); return(results); } // try each encoding encoding: foreach (var encoding in autoConnectEncoding) { // try each encryption mode encryption: foreach (var encryption in autoConnectEncryption) { // try each SSL protocol protocol: foreach (var protocol in autoConnectProtocols) { // skip secure protocols if testing plain FTP if (encryption == FtpEncryptionMode.None && protocol != SysSslProtocols.None) { continue; } // try each data connection type dataType: foreach (var dataType in autoConnectData) { // clone this connection var conn = CloneConnection(); // set basic props conn.Host = Host; conn.Port = Port; conn.Credentials = Credentials; // set rolled props conn.EncryptionMode = encryption; conn.SslProtocols = protocol; conn.DataConnectionType = dataType; conn.Encoding = encoding; // try to connect var connected = false; try { conn.Connect(); connected = true; conn.Dispose(); } catch (Exception ex) { conn.Dispose(); // catch error "no such host is known" and hard abort #if NETFX || CORE2PLUS if (ex is SocketException && ((SocketException)ex).ErrorCode == 11001) { return(results); } #else if (ex is SocketException && ((SocketException)ex).Message.Contains("No such host is known")) { return(results); } #endif // catch error "timed out trying to connect" and hard abort if (ex is TimeoutException) { return(results); } } // if it worked, add the profile if (connected) { results.Add(new FtpProfile { Host = Host, Credentials = Credentials, Encryption = encryption, Protocols = protocol, DataConnection = dataType, Encoding = encoding }); // stop if only 1 wanted if (firstOnly) { return(results); } } } } } } #if !CORE14 } #endif return(results); }