javaftpftp-clientftp-serverpassive-mode

Problems to transfer files using FTP Apache Java implementation


I'm transferring files using FTP with JAVA. I'm using FTPClient and FTPServer from Apache. But in a specific environment, sometimes the file isn't transferred. I call the enterLocalPassiveMode method from FTPClient before the login method call and yet sometimes the files aren't transferred.

  1. The storeFile method returns "false".
  2. The getReplyString method returns "200 Command TYPE okay".
  3. The list method returns "227".

When the file is transferred successfully:

  1. The list method returns 150.
  2. The getReplyString method returns "150 File status okay; about to open data connection".

Client code:

        this.getFtpClientUtil().connect(settingsService.getServer(), settingsService.getFtpServerCommandChannelPort());

        int reply = this.getFtpClientUtil().getReplyCode();

        if (!FTPReply.isPositiveCompletion(reply)) {
            logger.error("Fail to connect to FTP Server");
            this.getFtpClientUtil().disconnect();
            isConnect = false;
            return false;
        }

        logger.info("FTP Server connected successfully");
        this.getFtpClientUtil().enterLocalPassiveMode();
        isConnect = this.getFtpClientUtil().login(settingsService.getftpUsername(), settingsService.getftpPassword());

Server Code:

        // Data Configuration
        this.dataConfigurationFactory = new DataConnectionConfigurationFactory();
        dataConfigurationFactory.setPassiveAddress(this.ipAddress);
        dataConfigurationFactory.setPassiveExternalAddress(this.ipAddress);
        dataConfigurationFactory.setPassivePorts(this.settingsService.getFtpServerTransferChannelPort());

        // Listener
        listenerFactory = new ListenerFactory();
        listenerFactory.setPort(this.settingsService.getFtpServerCommandChannelPort());
        listenerFactory.setDataConnectionConfiguration(dataConfigurationFactory.createDataConnectionConfiguration());

        // Desliga SSL
        listenerFactory.setImplicitSsl(false);

        // User
        PropertiesUserManagerFactory userManagerFactory = new PropertiesUserManagerFactory();
        UserManager um = userManagerFactory.createUserManager();

        this.serverFactory = new FtpServerFactory();
        serverFactory.addListener("default", listenerFactory.createListener());
        serverFactory.setUserManager(um);

        UserFactory userFactory = new UserFactory();
        userFactory.setName(this.settingsService.getftpUsername());
        userFactory.setPassword(this.settingsService.getftpPassword());
        userFactory.setMaxIdleTime(60);

        final String absolutePath = this.settingsService.getftpHomeDirectory();

        userFactory.setHomeDirectory(absolutePath);
        userFactory.setAuthorities(Arrays.asList((Authority) new WritePermission()));

        User user = userFactory.createUser();
        um.save(user);

        // Connection Config
        connectionConfigFactory = new ConnectionConfigFactory();
        connectionConfigFactory.setAnonymousLoginEnabled(false);
        connectionConfigFactory.setMaxLogins(100);
        serverFactory.setConnectionConfig(connectionConfigFactory.createConnectionConfig());

Is this a firewall problem?

I tried set passive port ranges on the server side using the setPassivePorts method from DataConnectionConfigurationFactory class, but the problem continues.

Is there any form to set port ranges on the Client side? How can I check if the connection is really using passive mode?

Thanks in advance.


Solution

  • Is this a firewall problem?

    In these cases, the correct way would be turn off the firewall to test or set a range of ports that the application can to connect. But in the production environment this wasn't an option. So, the only thing I know is that in the development environment works fine and in the production environment the problem arise. My assumption is that the firewall is blocking the ports.

    Is there any form to set port ranges on the Client side? How can I check if the connection is really using passive mode?

    The local port can be set on connect() method from FTPClient:

    this.getFtpClientUtil().connect(settingsService.getServer(), settingsService.getFtpServerCommandChannelPort(), 
                    InetAddress.getLocalHost(), getLocalPort());
    

    It's possible to verify if the connection is using the passive mode in two ways:

    1. The getPassivePort() method from FTPClient will return -1 if the passive mode isn't in use.
    2. In the question, I mentioned that the list() method from FTPClient returns the 227 code. This code also indicates that the passive mode is in use. I thought that this code was an error. What really happened was an error after entering in passive mode, but was returned the code from previous command.

    Setting local port didn't resolve the problem. Perhaps because the local port that I'm configuring is only for the command channel. The transfer channel still uses random ports on client side. The solution is trying to transfer the file again. If the transfer fails, I disconnect the client, then I connect the client again using other ports and then I try to transfer the file again. In all my tests, the files was transferred successfully in the second time. I put 3 attempts if the transferring fails.

    When connecting the client again, if the same port was used, the java could throw "java.net.BindException: Address already in use". Even after disconnect the client, the port continues locked for a few moments. In this case, I verify if the port is available before trying to connect, if necessary I try to connect the client in another port.