/// <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);
        }