/// <summary> /// Opens the specified type of passive data stream /// </summary> /// <param name="type">Type of passive data stream to open</param> /// <param name="command">The command to execute that requires a data stream</param> /// <param name="restart">Restart location in bytes for file transfer</param> /// <returns>A data stream ready to be used</returns> FtpDataStream OpenPassiveDataStream(FtpDataConnectionType type, string command, long restart) { FtpTrace.WriteFunc("OpenPassiveDataStream", new object[] { type, command, restart }); FtpDataStream stream = null; FtpReply reply; Match m; string host = null; int port = 0; if (m_stream == null) { throw new InvalidOperationException("The control connection stream is null! Generally this means there is no connection to the server. Cannot open a passive data stream."); } if (type == FtpDataConnectionType.EPSV || type == FtpDataConnectionType.AutoPassive) { if (!(reply = Execute("EPSV")).Success) { // if we're connected with IPv4 and data channel type is AutoPassive then fallback to IPv4 if (reply.Type == FtpResponseType.PermanentNegativeCompletion && type == FtpDataConnectionType.AutoPassive && m_stream != null && m_stream.LocalEndPoint.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) { return(OpenPassiveDataStream(FtpDataConnectionType.PASV, command, restart)); } throw new FtpCommandException(reply); } m = Regex.Match(reply.Message, @"\(\|\|\|(?<port>\d+)\|\)"); if (!m.Success) { throw new FtpException("Failed to get the EPSV port from: " + reply.Message); } host = m_host; port = int.Parse(m.Groups["port"].Value); } else { if (m_stream.LocalEndPoint.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork) { throw new FtpException("Only IPv4 is supported by the PASV command. Use EPSV instead."); } if (!(reply = Execute("PASV")).Success) { throw new FtpCommandException(reply); } m = Regex.Match(reply.Message, @"(?<quad1>\d+)," + @"(?<quad2>\d+)," + @"(?<quad3>\d+)," + @"(?<quad4>\d+)," + @"(?<port1>\d+)," + @"(?<port2>\d+)"); if (!m.Success || m.Groups.Count != 7) { throw new FtpException(("Malformed PASV response: " + reply.Message)); } // PASVEX mode ignores the host supplied in the PASV response if (type == FtpDataConnectionType.PASVEX) { host = m_host; } else { host = (m.Groups["quad1"].Value + "." + m.Groups["quad2"].Value + "." + m.Groups["quad3"].Value + "." + m.Groups["quad4"].Value); } port = (int.Parse(m.Groups["port1"].Value) << 8) + int.Parse(m.Groups["port2"].Value); } stream = new FtpDataStream(this); stream.ConnectTimeout = DataConnectionConnectTimeout; stream.ReadTimeout = DataConnectionReadTimeout; Connect(stream, host, port, InternetProtocolVersions); stream.SetSocketOption(System.Net.Sockets.SocketOptionLevel.Socket, System.Net.Sockets.SocketOptionName.KeepAlive, m_keepAlive); if (restart > 0) { if (!(reply = Execute("REST " + restart)).Success) { throw new FtpCommandException(reply); } } if (!(reply = Execute(command)).Success) { stream.Close(); throw new FtpCommandException(reply); } // the command status is used to determine // if a reply needs to be read from the server // when the stream is closed so always set it // otherwise things can get out of sync. stream.CommandStatus = reply; #if !NO_SSL // this needs to take place after the command is executed if (m_dataConnectionEncryption && m_encryptionmode != FtpEncryptionMode.None) { stream.ActivateEncryption(m_host, this.ClientCertificates.Count > 0 ? this.ClientCertificates : null, m_SslProtocols); } #endif return(stream); }
/// <summary> /// Opens the specified type of active data stream /// </summary> /// <param name="type">Type of passive data stream to open</param> /// <param name="command">The command to execute that requires a data stream</param> /// <param name="restart">Restart location in bytes for file transfer</param> /// <returns>A data stream ready to be used</returns> FtpDataStream OpenActiveDataStream(FtpDataConnectionType type, string command, long restart) { FtpTrace.WriteFunc("OpenActiveDataStream", new object[] { type, command, restart }); FtpDataStream stream = new FtpDataStream(this); FtpReply reply; #if !CORE IAsyncResult ar; #endif if (m_stream == null) { throw new InvalidOperationException("The control connection stream is null! Generally this means there is no connection to the server. Cannot open an active data stream."); } if (m_ActivePorts == null || !m_ActivePorts.Any()) { // Use random port stream.Listen(m_stream.LocalEndPoint.Address, 0); } else { var success = false; // Use one of the specified ports foreach (var port in m_ActivePorts) { try { stream.Listen(m_stream.LocalEndPoint.Address, port); success = true; } catch (SocketException se) { #if NETFX // Already in use if (se.ErrorCode != 10048) { throw; } #else throw; #endif } } // No usable port found if (!success) { throw new Exception("No valid active data port available!"); } } #if !CORE ar = stream.BeginAccept(null, null); #endif if (type == FtpDataConnectionType.EPRT || type == FtpDataConnectionType.AutoActive) { int ipver = 0; switch (stream.LocalEndPoint.AddressFamily) { case System.Net.Sockets.AddressFamily.InterNetwork: ipver = 1; // IPv4 break; case System.Net.Sockets.AddressFamily.InterNetworkV6: ipver = 2; // IPv6 break; default: throw new InvalidOperationException("The IP protocol being used is not supported."); } if (!(reply = Execute("EPRT |" + ipver + "|" + GetLocalAddress(stream.LocalEndPoint.Address) + "|" + stream.LocalEndPoint.Port + "|")).Success) { // if we're connected with IPv4 and the data channel type is AutoActive then try to fall back to the PORT command if (reply.Type == FtpResponseType.PermanentNegativeCompletion && type == FtpDataConnectionType.AutoActive && m_stream != null && m_stream.LocalEndPoint.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) { stream.ControlConnection = null; // we don't want this failed EPRT attempt to close our control connection when the stream is closed so clear out the reference. stream.Close(); return(OpenActiveDataStream(FtpDataConnectionType.PORT, command, restart)); } else { stream.Close(); throw new FtpCommandException(reply); } } } else { if (m_stream.LocalEndPoint.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork) { throw new FtpException("Only IPv4 is supported by the PORT command. Use EPRT instead."); } if (!(reply = Execute("PORT " + GetLocalAddress(stream.LocalEndPoint.Address).Replace('.', ',') + "," + stream.LocalEndPoint.Port / 256 + "," + stream.LocalEndPoint.Port % 256)).Success) { stream.Close(); throw new FtpCommandException(reply); } } if (restart > 0) { if (!(reply = Execute("REST " + restart)).Success) { throw new FtpCommandException(reply); } } if (!(reply = Execute(command)).Success) { stream.Close(); throw new FtpCommandException(reply); } // the command status is used to determine // if a reply needs to be read from the server // when the stream is closed so always set it // otherwise things can get out of sync. stream.CommandStatus = reply; #if CORE stream.AcceptAsync().Wait(); #else ar.AsyncWaitHandle.WaitOne(m_dataConnectionConnectTimeout); if (!ar.IsCompleted) { stream.Close(); throw new TimeoutException("Timed out waiting for the server to connect to the active data socket."); } stream.EndAccept(ar); #endif #if !NO_SSL if (m_dataConnectionEncryption && m_encryptionmode != FtpEncryptionMode.None) { stream.ActivateEncryption(m_host, this.ClientCertificates.Count > 0 ? this.ClientCertificates : null, m_SslProtocols); } #endif stream.SetSocketOption(System.Net.Sockets.SocketOptionLevel.Socket, System.Net.Sockets.SocketOptionName.KeepAlive, m_keepAlive); stream.ReadTimeout = m_dataConnectionReadTimeout; return(stream); }