Please note that the patch is not even worh considering...for
instance, i am trying to close the port  without any checks that we
are using passive mode!

As Sai suggests, if that's ok for you I would simply return an error
code if there are no free available ports in 1.0.x. But, since some
users (Fred) are expecting that PASV requests get enqueued I don't
know if we should change FTPServer behaviour at this moment.





2010/3/25 Sai Pullabhotla <[email protected]>:
> I would like to make a comment on the current implementation -
>
> Do you think we want to wait indefinitely when there is no available
> passive port, or should we have a timed wait, say 60 seconds. If we
> can't get an available passive port in the timeout period, we should
> send an error back to the client. This is mostly based on the fact
> that most FTP clients would have their data connection timeout set,
> which is very small. Or even better, since the current version is not
> supposed to be used with too little passive ports than the number of
> concurrent clients, should we error out right way with 4XX message
> indicating a data connection could not be opened?
>
> Regards,
> Sai Pullabhotla
>
>
>
>
>
> On Thu, Mar 25, 2010 at 7:51 AM, Niklas Gustavsson <[email protected]> 
> wrote:
>> Interesting! Could you send the patch without whitespace/formatting
>> changes, would make it much easier to follow.
>>
>> /niklas
>>
>> On Thu, Mar 25, 2010 at 1:46 PM, David Latorre <[email protected]> wrote:
>>> hello Sai,
>>>
>>> I didn't have time to fully review the code but i made these nearly
>>> random  changes in IODataConnectionFactory and
>>> DefaultDataConnectionConfiguration and the server is not getting
>>> blocked anymore...
>>>
>>>
>>>
>>> Index: 
>>> main/java/org/apache/ftpserver/impl/DefaultDataConnectionConfiguration.java
>>> ===================================================================
>>> --- 
>>> main/java/org/apache/ftpserver/impl/DefaultDataConnectionConfiguration.java 
>>> (revision
>>> 927354)
>>> +++ 
>>> main/java/org/apache/ftpserver/impl/DefaultDataConnectionConfiguration.java 
>>> (working
>>> copy)
>>> @@ -129,7 +129,8 @@
>>>      * port will be used.
>>>      */
>>>     public synchronized int requestPassivePort() {
>>> -        int dataPort = -1;
>>> +
>>> +       int dataPort = -1;
>>>         int loopTimes = 2;
>>>         Thread currThread = Thread.currentThread();
>>>
>>> @@ -164,8 +165,7 @@
>>>      */
>>>     public synchronized void releasePassivePort(final int port) {
>>>         passivePorts.releasePort(port);
>>> -
>>> -        notify();
>>> +        notifyAll();
>>>     }
>>>
>>>     /**
>>> Index: main/java/org/apache/ftpserver/impl/IODataConnectionFactory.java
>>> ===================================================================
>>> --- main/java/org/apache/ftpserver/impl/IODataConnectionFactory.java    
>>> (revision
>>> 927354)
>>> +++ main/java/org/apache/ftpserver/impl/IODataConnectionFactory.java    
>>> (working
>>> copy)
>>> @@ -44,446 +44,459 @@
>>>  *
>>>  * We can get the FTP data connection using this class. It uses either PORT 
>>> or
>>>  * PASV command.
>>> - *
>>> + *
>>>  * @author <a href="http://mina.apache.org";>Apache MINA Project</a>
>>>  */
>>>  public class IODataConnectionFactory implements 
>>> ServerDataConnectionFactory {
>>>
>>> -    private final Logger LOG = LoggerFactory
>>> -            .getLogger(IODataConnectionFactory.class);
>>> +       private final Logger LOG = LoggerFactory
>>> +                       .getLogger(IODataConnectionFactory.class);
>>>
>>> -    private FtpServerContext serverContext;
>>> +       private FtpServerContext serverContext;
>>>
>>> -    private Socket dataSoc;
>>> +       private Socket dataSoc;
>>>
>>> -    ServerSocket servSoc;
>>> +       ServerSocket servSoc;
>>>
>>> -    InetAddress address;
>>> +       InetAddress address;
>>>
>>> -    int port = 0;
>>> +       int port = 0;
>>>
>>> -    long requestTime = 0L;
>>> +       long requestTime = 0L;
>>>
>>> -    boolean passive = false;
>>> +       boolean passive = false;
>>>
>>> -    boolean secure = false;
>>> +       boolean secure = false;
>>>
>>> -    private boolean isZip = false;
>>> +       private boolean isZip = false;
>>>
>>> -    InetAddress serverControlAddress;
>>> +       InetAddress serverControlAddress;
>>>
>>> -    FtpIoSession session;
>>> +       FtpIoSession session;
>>>
>>> -    public IODataConnectionFactory(final FtpServerContext serverContext,
>>> -            final FtpIoSession session) {
>>> -        this.session = session;
>>> -        this.serverContext = serverContext;
>>> -        if (session.getListener().getDataConnectionConfiguration()
>>> -                .isImplicitSsl()) {
>>> -            secure = true;
>>> -        }
>>> -    }
>>> +       public IODataConnectionFactory(final FtpServerContext serverContext,
>>> +                       final FtpIoSession session) {
>>> +               this.session = session;
>>> +               this.serverContext = serverContext;
>>> +               if (session.getListener().getDataConnectionConfiguration()
>>> +                               .isImplicitSsl()) {
>>> +                       secure = true;
>>> +               }
>>> +       }
>>>
>>> -    /**
>>> -     * Close data socket.
>>> -     * This method must be idempotent as we might call it multiple
>>> times during disconnect.
>>> -     */
>>> -    public synchronized void closeDataConnection() {
>>> +       /**
>>> +        * Close data socket. This method must be idempotent as we might 
>>> call it
>>> +        * multiple times during disconnect.
>>> +        */
>>> +       public synchronized void closeDataConnection() {
>>>
>>> -        // close client socket if any
>>> -        if (dataSoc != null) {
>>> -            try {
>>> -                dataSoc.close();
>>> -            } catch (Exception ex) {
>>> -                LOG.warn("FtpDataConnection.closeDataSocket()", ex);
>>> -            }
>>> -            dataSoc = null;
>>> -        }
>>> +               // close client socket if any
>>> +               if (dataSoc != null) {
>>> +                       try {
>>> +                               dataSoc.close();
>>> +                       } catch (Exception ex) {
>>> +                               
>>> LOG.warn("FtpDataConnection.closeDataSocket()", ex);
>>> +                       }
>>> +                       dataSoc = null;
>>> +               }
>>>
>>> -        // close server socket if any
>>> -        if (servSoc != null) {
>>> -            try {
>>> -                servSoc.close();
>>> -            } catch (Exception ex) {
>>> -                LOG.warn("FtpDataConnection.closeDataSocket()", ex);
>>> -            }
>>> +               // close server socket if any
>>> +               if (servSoc != null) {
>>> +                       try {
>>> +                               servSoc.close();
>>> +                       } catch (Exception ex) {
>>> +                               
>>> LOG.warn("FtpDataConnection.closeDataSocket()", ex);
>>> +                       }
>>>
>>> -            if (session != null) {
>>> -                DataConnectionConfiguration dcc = session.getListener()
>>> -                        .getDataConnectionConfiguration();
>>> -                if (dcc != null) {
>>> -                    dcc.releasePassivePort(port);
>>> -                }
>>> -            }
>>> +                       servSoc = null;
>>> +               }
>>>
>>> -            servSoc = null;
>>> -        }
>>> +               // release passive ports
>>> +                if (session != null) {
>>> +             DataConnectionConfiguration dcc = session.getListener()
>>> +                     .getDataConnectionConfiguration();
>>> +             if (dcc != null) {
>>> +                 dcc.releasePassivePort(port);
>>> +             }
>>> +         }
>>> +
>>> +               // reset request time
>>> +               requestTime = 0L;
>>> +       }
>>>
>>> -        // reset request time
>>> -        requestTime = 0L;
>>> -    }
>>> +       /**
>>> +        * Port command.
>>> +        */
>>> +       public synchronized void initActiveDataConnection(
>>> +                       final InetSocketAddress address) {
>>>
>>> -    /**
>>> -     * Port command.
>>> -     */
>>> -    public synchronized void initActiveDataConnection(
>>> -            final InetSocketAddress address) {
>>> +               // close old sockets if any
>>> +               closeDataConnection();
>>>
>>> -        // close old sockets if any
>>> -        closeDataConnection();
>>> +               // set variables
>>> +               passive = false;
>>> +               this.address = address.getAddress();
>>> +               port = address.getPort();
>>> +               requestTime = System.currentTimeMillis();
>>> +       }
>>>
>>> -        // set variables
>>> -        passive = false;
>>> -        this.address = address.getAddress();
>>> -        port = address.getPort();
>>> -        requestTime = System.currentTimeMillis();
>>> -    }
>>> +       private SslConfiguration getSslConfiguration() {
>>> +               DataConnectionConfiguration dataCfg = session.getListener()
>>> +                               .getDataConnectionConfiguration();
>>>
>>> -    private SslConfiguration getSslConfiguration() {
>>> -        DataConnectionConfiguration dataCfg = session.getListener()
>>> -                .getDataConnectionConfiguration();
>>> +               SslConfiguration configuration = 
>>> dataCfg.getSslConfiguration();
>>>
>>> -        SslConfiguration configuration = dataCfg.getSslConfiguration();
>>> +               // fall back if no configuration has been provided on the 
>>> data
>>> +               // connection config
>>> +               if (configuration == null) {
>>> +                       configuration = 
>>> session.getListener().getSslConfiguration();
>>> +               }
>>>
>>> -        // fall back if no configuration has been provided on the
>>> data connection config
>>> -        if (configuration == null) {
>>> -            configuration = session.getListener().getSslConfiguration();
>>> -        }
>>> +               return configuration;
>>> +       }
>>>
>>> -        return configuration;
>>> -    }
>>> +       /**
>>> +        * Initiate a data connection in passive mode (server listening).
>>> +        */
>>> +       public synchronized InetSocketAddress initPassiveDataConnection()
>>> +                       throws DataConnectionException {
>>> +               LOG.debug("Initiating passive data connection");
>>> +               // close old sockets if any
>>> +               closeDataConnection();
>>>
>>> -    /**
>>> -     * Initiate a data connection in passive mode (server listening).
>>> -     */
>>> -    public synchronized InetSocketAddress initPassiveDataConnection()
>>> -            throws DataConnectionException {
>>> -        LOG.debug("Initiating passive data connection");
>>> -        // close old sockets if any
>>> -        closeDataConnection();
>>> +               // get the passive port
>>> +               int passivePort = session.getListener()
>>> +                               
>>> .getDataConnectionConfiguration().requestPassivePort();
>>> +               if (passivePort == -1) {
>>> +                       servSoc = null;
>>> +                       throw new DataConnectionException(
>>> +                                       "Cannot find an available passive 
>>> port.");
>>> +               }
>>>
>>> -        // get the passive port
>>> -        int passivePort = session.getListener()
>>> -                .getDataConnectionConfiguration().requestPassivePort();
>>> -        if (passivePort == -1) {
>>> -            servSoc = null;
>>> -            throw new DataConnectionException(
>>> -                    "Cannot find an available passive port.");
>>> -        }
>>> +               // open passive server socket and get parameters
>>> +               try {
>>> +                       DataConnectionConfiguration dataCfg = 
>>> session.getListener()
>>> +                                       .getDataConnectionConfiguration();
>>>
>>> -        // open passive server socket and get parameters
>>> -        try {
>>> -            DataConnectionConfiguration dataCfg = session.getListener()
>>> -                    .getDataConnectionConfiguration();
>>> +                       String passiveAddress = dataCfg.getPassiveAddress();
>>>
>>> -            String passiveAddress = dataCfg.getPassiveAddress();
>>> +                       if (passiveAddress == null) {
>>> +                               address = serverControlAddress;
>>> +                       } else {
>>> +                               address = 
>>> resolveAddress(dataCfg.getPassiveAddress());
>>> +                       }
>>>
>>> -            if (passiveAddress == null) {
>>> -                address = serverControlAddress;
>>> -            } else {
>>> -                address = resolveAddress(dataCfg.getPassiveAddress());
>>> -            }
>>> +                       if (secure) {
>>> +                               LOG
>>> +                                               .debug(
>>> +                                                               "Opening 
>>> SSL passive data connection on address \"{}\" and port {}",
>>> +                                                               address, 
>>> passivePort);
>>> +                               SslConfiguration ssl = 
>>> getSslConfiguration();
>>> +                               if (ssl == null) {
>>> +                                       throw new DataConnectionException(
>>> +                                                       "Data connection 
>>> SSL required but not configured.");
>>> +                               }
>>>
>>> -            if (secure) {
>>> -                LOG
>>> -                        .debug(
>>> -                                "Opening SSL passive data connection
>>> on address \"{}\" and port {}",
>>> -                                address, passivePort);
>>> -                SslConfiguration ssl = getSslConfiguration();
>>> -                if (ssl == null) {
>>> -                    throw new DataConnectionException(
>>> -                            "Data connection SSL required but not
>>> configured.");
>>> -                }
>>> +                               // this method does not actually create the 
>>> SSL socket, due to a
>>> +                               // JVM bug
>>> +                               // 
>>> (https://issues.apache.org/jira/browse/FTPSERVER-241).
>>> +                               // Instead, it creates a regular
>>> +                               // ServerSocket that will be wrapped as a 
>>> SSL socket in
>>> +                               // createDataSocket()
>>> +                               servSoc = new ServerSocket(passivePort, 0, 
>>> address);
>>> +                               LOG
>>> +                                               .debug(
>>> +                                                               "SSL 
>>> Passive data connection created on address \"{}\" and port {}",
>>> +                                                               address, 
>>> passivePort);
>>> +                       } else {
>>> +                               LOG
>>> +                                               .debug(
>>> +                                                               "Opening 
>>> passive data connection on address \"{}\" and port {}",
>>> +                                                               address, 
>>> passivePort);
>>> +                               servSoc = new ServerSocket(passivePort, 0, 
>>> address);
>>> +                               LOG
>>> +                                               .debug(
>>> +                                                               "Passive 
>>> data connection created on address \"{}\" and port {}",
>>> +                                                               address, 
>>> passivePort);
>>> +                       }
>>> +                       port = servSoc.getLocalPort();
>>> +                       servSoc.setSoTimeout(dataCfg.getIdleTime() * 1000);
>>>
>>> -                // this method does not actually create the SSL
>>> socket, due to a JVM bug
>>> -                // (https://issues.apache.org/jira/browse/FTPSERVER-241).
>>> -                // Instead, it creates a regular
>>> -                // ServerSocket that will be wrapped as a SSL socket
>>> in createDataSocket()
>>> -                servSoc = new ServerSocket(passivePort, 0, address);
>>> -                LOG
>>> -                        .debug(
>>> -                                "SSL Passive data connection created
>>> on address \"{}\" and port {}",
>>> -                                address, passivePort);
>>> -            } else {
>>> -                LOG
>>> -                        .debug(
>>> -                                "Opening passive data connection on
>>> address \"{}\" and port {}",
>>> -                                address, passivePort);
>>> -                servSoc = new ServerSocket(passivePort, 0, address);
>>> -                LOG
>>> -                        .debug(
>>> -                                "Passive data connection created on
>>> address \"{}\" and port {}",
>>> -                                address, passivePort);
>>> -            }
>>> -            port = servSoc.getLocalPort();
>>> -            servSoc.setSoTimeout(dataCfg.getIdleTime() * 1000);
>>> +                       // set different state variables
>>> +                       passive = true;
>>> +                       requestTime = System.currentTimeMillis();
>>>
>>> -            // set different state variables
>>> -            passive = true;
>>> -            requestTime = System.currentTimeMillis();
>>> +                       return new InetSocketAddress(address, port);
>>> +               } catch (Exception ex) {
>>> +                       servSoc = null;
>>> +                       closeDataConnection();
>>> +                       throw new DataConnectionException(
>>> +                                       "Failed to initate passive data 
>>> connection: "
>>> +                                                       + ex.getMessage(), 
>>> ex);
>>> +               }
>>> +       }
>>>
>>> -            return new InetSocketAddress(address, port);
>>> -        } catch (Exception ex) {
>>> -            servSoc = null;
>>> -            closeDataConnection();
>>> -            throw new DataConnectionException(
>>> -                    "Failed to initate passive data connection: "
>>> -                            + ex.getMessage(), ex);
>>> -        }
>>> -    }
>>> +       /*
>>> +        * (non-Javadoc)
>>> +        *
>>> +        * @see 
>>> org.apache.ftpserver.FtpDataConnectionFactory2#getInetAddress()
>>> +        */
>>> +       public InetAddress getInetAddress() {
>>> +               return address;
>>> +       }
>>>
>>> -    /*
>>> -     * (non-Javadoc)
>>> -     *
>>> -     * @see org.apache.ftpserver.FtpDataConnectionFactory2#getInetAddress()
>>> -     */
>>> -    public InetAddress getInetAddress() {
>>> -        return address;
>>> -    }
>>> +       /*
>>> +        * (non-Javadoc)
>>> +        *
>>> +        * @see org.apache.ftpserver.FtpDataConnectionFactory2#getPort()
>>> +        */
>>> +       public int getPort() {
>>> +               return port;
>>> +       }
>>>
>>> -    /*
>>> -     * (non-Javadoc)
>>> -     *
>>> -     * @see org.apache.ftpserver.FtpDataConnectionFactory2#getPort()
>>> -     */
>>> -    public int getPort() {
>>> -        return port;
>>> -    }
>>> +       /*
>>> +        * (non-Javadoc)
>>> +        *
>>> +        * @see 
>>> org.apache.ftpserver.FtpDataConnectionFactory2#openConnection()
>>> +        */
>>> +       public DataConnection openConnection() throws Exception {
>>> +               return new IODataConnection(createDataSocket(), session, 
>>> this);
>>> +       }
>>>
>>> -    /*
>>> -     * (non-Javadoc)
>>> -     *
>>> -     * @see org.apache.ftpserver.FtpDataConnectionFactory2#openConnection()
>>> -     */
>>> -    public DataConnection openConnection() throws Exception {
>>> -        return new IODataConnection(createDataSocket(), session, this);
>>> -    }
>>> +       /**
>>> +        * Get the data socket. In case of error returns null.
>>> +        */
>>> +       private synchronized Socket createDataSocket() throws Exception {
>>>
>>> -    /**
>>> -     * Get the data socket. In case of error returns null.
>>> -     */
>>> -    private synchronized Socket createDataSocket() throws Exception {
>>> +               // get socket depending on the selection
>>> +               dataSoc = null;
>>> +               DataConnectionConfiguration dataConfig = 
>>> session.getListener()
>>> +                               .getDataConnectionConfiguration();
>>> +               try {
>>> +                       if (!passive) {
>>> +                               if (secure) {
>>> +                                       LOG.debug("Opening secure active 
>>> data connection");
>>> +                                       SslConfiguration ssl = 
>>> getSslConfiguration();
>>> +                                       if (ssl == null) {
>>> +                                               throw new FtpException(
>>> +                                                               "Data 
>>> connection SSL not configured");
>>> +                                       }
>>>
>>> -        // get socket depending on the selection
>>> -        dataSoc = null;
>>> -        DataConnectionConfiguration dataConfig = session.getListener()
>>> -                .getDataConnectionConfiguration();
>>> -        try {
>>> -            if (!passive) {
>>> -                if (secure) {
>>> -                    LOG.debug("Opening secure active data connection");
>>> -                    SslConfiguration ssl = getSslConfiguration();
>>> -                    if (ssl == null) {
>>> -                        throw new FtpException(
>>> -                                "Data connection SSL not configured");
>>> -                    }
>>> +                                       // get socket factory
>>> +                                       SSLSocketFactory socFactory = 
>>> ssl.getSocketFactory();
>>>
>>> -                    // get socket factory
>>> -                    SSLSocketFactory socFactory = ssl.getSocketFactory();
>>> +                                       // create socket
>>> +                                       SSLSocket ssoc = (SSLSocket) 
>>> socFactory.createSocket();
>>> +                                       ssoc.setUseClientMode(false);
>>>
>>> -                    // create socket
>>> -                    SSLSocket ssoc = (SSLSocket) socFactory.createSocket();
>>> -                    ssoc.setUseClientMode(false);
>>> +                                       // initialize socket
>>> +                                       if (ssl.getEnabledCipherSuites() != 
>>> null) {
>>> +                                               
>>> ssoc.setEnabledCipherSuites(ssl
>>> +                                                               
>>> .getEnabledCipherSuites());
>>> +                                       }
>>> +                                       dataSoc = ssoc;
>>> +                               } else {
>>> +                                       LOG.debug("Opening active data 
>>> connection");
>>> +                                       dataSoc = new Socket();
>>> +                               }
>>>
>>> -                    // initialize socket
>>> -                    if (ssl.getEnabledCipherSuites() != null) {
>>> -
>>> ssoc.setEnabledCipherSuites(ssl.getEnabledCipherSuites());
>>> -                    }
>>> -                    dataSoc = ssoc;
>>> -                } else {
>>> -                    LOG.debug("Opening active data connection");
>>> -                    dataSoc = new Socket();
>>> -                }
>>> +                               dataSoc.setReuseAddress(true);
>>>
>>> -                dataSoc.setReuseAddress(true);
>>> +                               InetAddress localAddr = 
>>> resolveAddress(dataConfig
>>> +                                               .getActiveLocalAddress());
>>>
>>> -                InetAddress localAddr = resolveAddress(dataConfig
>>> -                        .getActiveLocalAddress());
>>> +                               // if no local address has been configured, 
>>> make sure we use the
>>> +                               // same as the client connects from
>>> +                               if (localAddr == null) {
>>> +                                       localAddr = ((InetSocketAddress) 
>>> session.getLocalAddress())
>>> +                                                       .getAddress();
>>> +                               }
>>>
>>> -                // if no local address has been configured, make sure
>>> we use the same as the client connects from
>>> -                if(localAddr == null) {
>>> -                    localAddr =
>>> ((InetSocketAddress)session.getLocalAddress()).getAddress();
>>> -                }
>>> +                               SocketAddress localSocketAddress = new 
>>> InetSocketAddress(
>>> +                                               localAddr, 
>>> dataConfig.getActiveLocalPort());
>>>
>>> -                SocketAddress localSocketAddress = new
>>> InetSocketAddress(localAddr, dataConfig.getActiveLocalPort());
>>> -
>>> -                LOG.debug("Binding active data connection to {}",
>>> localSocketAddress);
>>> -                dataSoc.bind(localSocketAddress);
>>> +                               LOG.debug("Binding active data connection 
>>> to {}",
>>> +                                               localSocketAddress);
>>> +                               dataSoc.bind(localSocketAddress);
>>>
>>> -                dataSoc.connect(new InetSocketAddress(address, port));
>>> -            } else {
>>> +                               dataSoc.connect(new 
>>> InetSocketAddress(address, port));
>>> +                       } else {
>>>
>>> -                if (secure) {
>>> -                    LOG.debug("Opening secure passive data connection");
>>> -                    // this is where we wrap the unsecured socket as
>>> a SSLSocket. This is
>>> -                    // due to the JVM bug described in FTPSERVER-241.
>>> +                               if (secure) {
>>> +                                       LOG.debug("Opening secure passive 
>>> data connection");
>>> +                                       // this is where we wrap the 
>>> unsecured socket as a
>>> +                                       // SSLSocket. This is
>>> +                                       // due to the JVM bug described in 
>>> FTPSERVER-241.
>>>
>>> -                    // get server socket factory
>>> -                    SslConfiguration ssl = getSslConfiguration();
>>> -
>>> -                    // we've already checked this, but let's do it again
>>> -                    if (ssl == null) {
>>> -                        throw new FtpException(
>>> -                                "Data connection SSL not configured");
>>> -                    }
>>> +                                       // get server socket factory
>>> +                                       SslConfiguration ssl = 
>>> getSslConfiguration();
>>>
>>> -                    SSLSocketFactory ssocketFactory = 
>>> ssl.getSocketFactory();
>>> +                                       // we've already checked this, but 
>>> let's do it again
>>> +                                       if (ssl == null) {
>>> +                                               throw new FtpException(
>>> +                                                               "Data 
>>> connection SSL not configured");
>>> +                                       }
>>>
>>> -                    Socket serverSocket = servSoc.accept();
>>> +                                       SSLSocketFactory ssocketFactory = 
>>> ssl.getSocketFactory();
>>>
>>> -                    SSLSocket sslSocket = (SSLSocket) ssocketFactory
>>> -                            .createSocket(serverSocket, serverSocket
>>> -                                    .getInetAddress().getHostAddress(),
>>> -                                    serverSocket.getPort(), true);
>>> -                    sslSocket.setUseClientMode(false);
>>> +                                       Socket serverSocket = 
>>> servSoc.accept();
>>>
>>> -                    // initialize server socket
>>> -                    if (ssl.getClientAuth() == ClientAuth.NEED) {
>>> -                        sslSocket.setNeedClientAuth(true);
>>> -                    } else if (ssl.getClientAuth() == ClientAuth.WANT) {
>>> -                        sslSocket.setWantClientAuth(true);
>>> -                    }
>>> +                                       SSLSocket sslSocket = (SSLSocket) 
>>> ssocketFactory
>>> +                                                       
>>> .createSocket(serverSocket, serverSocket
>>> +                                                                       
>>> .getInetAddress().getHostAddress(),
>>> +                                                                       
>>> serverSocket.getPort(), true);
>>> +                                       sslSocket.setUseClientMode(false);
>>>
>>> -                    if (ssl.getEnabledCipherSuites() != null) {
>>> -                        sslSocket.setEnabledCipherSuites(ssl
>>> -                                .getEnabledCipherSuites());
>>> -                    }
>>> +                                       // initialize server socket
>>> +                                       if (ssl.getClientAuth() == 
>>> ClientAuth.NEED) {
>>> +                                               
>>> sslSocket.setNeedClientAuth(true);
>>> +                                       } else if (ssl.getClientAuth() == 
>>> ClientAuth.WANT) {
>>> +                                               
>>> sslSocket.setWantClientAuth(true);
>>> +                                       }
>>>
>>> -                    dataSoc = sslSocket;
>>> -                } else {
>>> -                    LOG.debug("Opening passive data connection");
>>> +                                       if (ssl.getEnabledCipherSuites() != 
>>> null) {
>>> +                                               
>>> sslSocket.setEnabledCipherSuites(ssl
>>> +                                                               
>>> .getEnabledCipherSuites());
>>> +                                       }
>>>
>>> -                    dataSoc = servSoc.accept();
>>> -                }
>>> -
>>> -                if (dataConfig.isPassiveIpCheck()) {
>>> +                                       dataSoc = sslSocket;
>>> +                               } else {
>>> +                                       LOG.debug("Opening passive data 
>>> connection");
>>> +
>>> +                                       dataSoc = servSoc.accept();
>>> +                               }
>>> +
>>> +                               if (dataConfig.isPassiveIpCheck()) {
>>>                                        // Let's make sure we got the 
>>> connection from the same
>>>                                        // client that we are expecting
>>> -                                       InetAddress remoteAddress = 
>>> ((InetSocketAddress)
>>> session.getRemoteAddress()).getAddress();
>>> +                                       InetAddress remoteAddress = 
>>> ((InetSocketAddress) session
>>> +                                                       
>>> .getRemoteAddress()).getAddress();
>>>                                        InetAddress dataSocketAddress = 
>>> dataSoc.getInetAddress();
>>>                                        if 
>>> (!dataSocketAddress.equals(remoteAddress)) {
>>> -                                               LOG.warn("Passive IP Check 
>>> failed. Closing data connection from "
>>> -                                                       + dataSocketAddress
>>> -                                                       + " as it does not 
>>> match the expected address "
>>> -                                                       + remoteAddress);
>>> +                                               LOG
>>> +                                                               
>>> .warn("Passive IP Check failed. Closing data connection from "
>>> +                                                                           
>>>     + dataSocketAddress
>>> +                                                                           
>>>     + " as it does not match the expected address "
>>> +                                                                           
>>>     + remoteAddress);
>>>                                                closeDataConnection();
>>>                                                return null;
>>>                                        }
>>>                                }
>>> -
>>> -                DataConnectionConfiguration dataCfg = session.getListener()
>>> -                    .getDataConnectionConfiguration();
>>> -
>>> -                dataSoc.setSoTimeout(dataCfg.getIdleTime() * 1000);
>>> -                LOG.debug("Passive data connection opened");
>>> -            }
>>> -        } catch (Exception ex) {
>>> -            closeDataConnection();
>>> -            LOG.warn("FtpDataConnection.getDataSocket()", ex);
>>> -            throw ex;
>>> -        }
>>> -        dataSoc.setSoTimeout(dataConfig.getIdleTime() * 1000);
>>>
>>> -        // Make sure we initiate the SSL handshake, or we'll
>>> -        // get an error if we turn out not to send any data
>>> -        // e.g. during the listing of an empty directory
>>> -        if (dataSoc instanceof SSLSocket) {
>>> -            ((SSLSocket) dataSoc).startHandshake();
>>> -        }
>>> +                               DataConnectionConfiguration dataCfg = 
>>> session.getListener()
>>> +                                               
>>> .getDataConnectionConfiguration();
>>>
>>> -        return dataSoc;
>>> -    }
>>> +                               dataSoc.setSoTimeout(dataCfg.getIdleTime() 
>>> * 1000);
>>> +                               LOG.debug("Passive data connection opened");
>>> +                       }
>>> +               } catch (Exception ex) {
>>> +                       closeDataConnection();
>>> +                       LOG.warn("FtpDataConnection.getDataSocket()", ex);
>>> +                       throw ex;
>>> +               }
>>> +               dataSoc.setSoTimeout(dataConfig.getIdleTime() * 1000);
>>>
>>> -    /*
>>> -     *  (non-Javadoc)
>>> -     *   Returns an InetAddress object from a hostname or IP address.
>>> -     */
>>> -    private InetAddress resolveAddress(String host)
>>> -            throws DataConnectionException {
>>> -        if (host == null) {
>>> -            return null;
>>> -        } else {
>>> -            try {
>>> -                return InetAddress.getByName(host);
>>> -            } catch (UnknownHostException ex) {
>>> -                throw new DataConnectionException("Failed to resolve
>>> address", ex);
>>> -            }
>>> -        }
>>> -    }
>>> +               // Make sure we initiate the SSL handshake, or we'll
>>> +               // get an error if we turn out not to send any data
>>> +               // e.g. during the listing of an empty directory
>>> +               if (dataSoc instanceof SSLSocket) {
>>> +                       ((SSLSocket) dataSoc).startHandshake();
>>> +               }
>>>
>>> -    /*
>>> -     * (non-Javadoc)
>>> -     *
>>> -     * @see org.apache.ftpserver.DataConnectionFactory#isSecure()
>>> -     */
>>> -    public boolean isSecure() {
>>> -        return secure;
>>> -    }
>>> +               return dataSoc;
>>> +       }
>>>
>>> -    /**
>>> -     * Set the security protocol.
>>> -     */
>>> -    public void setSecure(final boolean secure) {
>>> -        this.secure = secure;
>>> -    }
>>> +       /*
>>> +        * (non-Javadoc) Returns an InetAddress object from a hostname or IP
>>> +        * address.
>>> +        */
>>> +       private InetAddress resolveAddress(String host)
>>> +                       throws DataConnectionException {
>>> +               if (host == null) {
>>> +                       return null;
>>> +               } else {
>>> +                       try {
>>> +                               return InetAddress.getByName(host);
>>> +                       } catch (UnknownHostException ex) {
>>> +                               throw new DataConnectionException("Failed 
>>> to resolve address",
>>> +                                               ex);
>>> +                       }
>>> +               }
>>> +       }
>>>
>>> -    /*
>>> -     * (non-Javadoc)
>>> -     *
>>> -     * @see org.apache.ftpserver.DataConnectionFactory#isZipMode()
>>> -     */
>>> -    public boolean isZipMode() {
>>> -        return isZip;
>>> -    }
>>> +       /*
>>> +        * (non-Javadoc)
>>> +        *
>>> +        * @see org.apache.ftpserver.DataConnectionFactory#isSecure()
>>> +        */
>>> +       public boolean isSecure() {
>>> +               return secure;
>>> +       }
>>>
>>> -    /**
>>> -     * Set zip mode.
>>> -     */
>>> -    public void setZipMode(final boolean zip) {
>>> -        isZip = zip;
>>> -    }
>>> +       /**
>>> +        * Set the security protocol.
>>> +        */
>>> +       public void setSecure(final boolean secure) {
>>> +               this.secure = secure;
>>> +       }
>>>
>>> -    /**
>>> -     * Check the data connection idle status.
>>> -     */
>>> -    public synchronized boolean isTimeout(final long currTime) {
>>> +       /*
>>> +        * (non-Javadoc)
>>> +        *
>>> +        * @see org.apache.ftpserver.DataConnectionFactory#isZipMode()
>>> +        */
>>> +       public boolean isZipMode() {
>>> +               return isZip;
>>> +       }
>>>
>>> -        // data connection not requested - not a timeout
>>> -        if (requestTime == 0L) {
>>> -            return false;
>>> -        }
>>> +       /**
>>> +        * Set zip mode.
>>> +        */
>>> +       public void setZipMode(final boolean zip) {
>>> +               isZip = zip;
>>> +       }
>>>
>>> -        // data connection active - not a timeout
>>> -        if (dataSoc != null) {
>>> -            return false;
>>> -        }
>>> +       /**
>>> +        * Check the data connection idle status.
>>> +        */
>>> +       public synchronized boolean isTimeout(final long currTime) {
>>>
>>> -        // no idle time limit - not a timeout
>>> -        int maxIdleTime = session.getListener()
>>> -                .getDataConnectionConfiguration().getIdleTime() * 1000;
>>> -        if (maxIdleTime == 0) {
>>> -            return false;
>>> -        }
>>> +               // data connection not requested - not a timeout
>>> +               if (requestTime == 0L) {
>>> +                       return false;
>>> +               }
>>>
>>> -        // idle time is within limit - not a timeout
>>> -        if ((currTime - requestTime) < maxIdleTime) {
>>> -            return false;
>>> -        }
>>> +               // data connection active - not a timeout
>>> +               if (dataSoc != null) {
>>> +                       return false;
>>> +               }
>>>
>>> -        return true;
>>> -    }
>>> +               // no idle time limit - not a timeout
>>> +               int maxIdleTime = session.getListener()
>>> +                               
>>> .getDataConnectionConfiguration().getIdleTime() * 1000;
>>> +               if (maxIdleTime == 0) {
>>> +                       return false;
>>> +               }
>>>
>>> -    /**
>>> -     * Dispose data connection - close all the sockets.
>>> -     */
>>> -    public void dispose() {
>>> -        closeDataConnection();
>>> -    }
>>> +               // idle time is within limit - not a timeout
>>> +               if ((currTime - requestTime) < maxIdleTime) {
>>> +                       return false;
>>> +               }
>>>
>>> -    /**
>>> -     * Sets the server's control address.
>>> -     */
>>> -    public void setServerControlAddress(final InetAddress
>>> serverControlAddress) {
>>> -        this.serverControlAddress = serverControlAddress;
>>> -    }
>>> +               return true;
>>> +       }
>>> +
>>> +       /**
>>> +        * Dispose data connection - close all the sockets.
>>> +        */
>>> +       public void dispose() {
>>> +               closeDataConnection();
>>> +       }
>>> +
>>> +       /**
>>> +        * Sets the server's control address.
>>> +        */
>>> +       public void setServerControlAddress(final InetAddress 
>>> serverControlAddress) {
>>> +               this.serverControlAddress = serverControlAddress;
>>> +       }
>>>  }
>>>
>>>
>>>
>>>
>>>
>>> 2010/3/25 Sai Pullabhotla <[email protected]>:
>>>> Just so we are all on the same page -
>>>>
>>>> Do you think the issue is with the LIST command, or the code that
>>>> creates passive data connection? Could you briefly explain some of the
>>>> issues that made you think it should be rewritten, so everyone gets a
>>>> chance to evaluate?
>>>>
>>>> Thanks.
>>>>
>>>> Regards,
>>>> Sai Pullabhotla
>>>>
>>>>
>>>>
>>>>
>>>>
>>>> On Thu, Mar 25, 2010 at 5:56 AM, David Latorre <[email protected]> wrote:
>>>>> 2010/3/24 Niklas Gustavsson <[email protected]>:
>>>>>> On Wed, Mar 24, 2010 at 6:03 PM, Fred Moore <[email protected]> 
>>>>>> wrote:
>>>>>>> we found an issue related to requestPassivePort() which may lead to an
>>>>>>> unresponsive V1.0.4 FTP (or FTP/S) Server, this issue can be reproduced.
>>>>>>>
>>>>>>> http://issues.apache.org/jira/browse/FTPSERVER-359 contains full 
>>>>>>> description
>>>>>>> of the symptoms and a minimalist java client and server to reproduce it.
>>>>>>
>>>>>> I haven't yet looked closer at the code you attached. But, I have seen
>>>>>> similar behavior myself during performance testing FtpServer. In that
>>>>>> case, I had a very similar behavior at around 20 threads. However, the
>>>>>> reason for the problem in that test was that the test case uses up
>>>>>> file handles (for the sockets) so fast that they will run out. Since
>>>>>> sockets hang around for some time also after closing, they were not
>>>>>> freed quickly enough and thus FtpServer could not open new ones. Could
>>>>>> you please verify that this is not the case here? You could look at
>>>>>> netstat output and look into increasing the allowed number of file
>>>>>> handles our the timeout time for sockets in your OS.
>>>>>
>>>>>
>>>>> Actually it is quite easy to reproduce this error (I just wrote a
>>>>> client test case with throws >20~30 threads that list a directory in
>>>>> the server ) and it's not file handle related:
>>>>>  we have several bugs in our code that cause this behaviour ,  i
>>>>> think we hould rewrite all the request/release passive port mechanism
>>>>> as there are several issues with it.
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>> /niklas
>>>>>>
>>>>>
>>>>
>>>
>>
>

Reply via email to