This is an automated email from the ASF dual-hosted git repository.

twolf pushed a commit to branch dev_3.0
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git


The following commit(s) were added to refs/heads/dev_3.0 by this push:
     new d29ae2448 GH-891: Add a test against ProFTPd
d29ae2448 is described below

commit d29ae2448f48d6929233ba463b88a49078245e1f
Author: Thomas Wolf <[email protected]>
AuthorDate: Thu Mar 26 22:27:17 2026 +0100

    GH-891: Add a test against ProFTPd
    
    Use docker container instantlinux/proftpd to run ProFTPd with SFTP
    enabled (password auth since pubkey auth would be tedious to set up,
    but that's OK for this test).
    
    Configure the container to send ProFTPd logs to stderr, not to some
    files inside the container. On stderr the test container's log consumer
    will pick it up.
    
    The test sets a low IGNORE_MESSAGE_FREQUENCY (every 20th message),
    uploads a file, downloads it again, and compares the contents.
    
    The test fails without the fix from commit e7f66e75 with the server
    disconnecting midway through the upload and shows the expected
    server-side log line "message format error: unable to read 2594667651
    bytes of string data (buflen = 20)" (or similar numbers).
    
    With the fix from commit e7f66e75 the test succeeds and the server-side
    log shows several lines "client sent SSH_MSG_IGNORE message (X bytes)"
    instead.
---
 .../org/apache/sshd/sftp/client/ProFtpdTest.java   | 122 +++++++++++++++++++++
 .../org/apache/sshd/sftp/client/proftpd.pwd        |   1 +
 2 files changed, 123 insertions(+)

diff --git 
a/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/ProFtpdTest.java 
b/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/ProFtpdTest.java
new file mode 100644
index 000000000..5a33411ee
--- /dev/null
+++ b/sshd-sftp/src/test/java/org/apache/sshd/sftp/client/ProFtpdTest.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.sftp.client;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.sshd.client.SshClient;
+import org.apache.sshd.client.session.ClientSession;
+import org.apache.sshd.common.Factory;
+import org.apache.sshd.common.random.Random;
+import org.apache.sshd.common.util.io.IoUtils;
+import org.apache.sshd.core.CoreModuleProperties;
+import org.apache.sshd.util.test.JUnitTestSupport;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.output.Slf4jLogConsumer;
+import org.testcontainers.images.builder.ImageFromDockerfile;
+import org.testcontainers.junit.jupiter.Container;
+import org.testcontainers.junit.jupiter.Testcontainers;
+import org.testcontainers.utility.MountableFile;
+
+@Testcontainers(disabledWithoutDocker = true)
+class ProFtpdTest extends JUnitTestSupport {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(ProFtpdTest.class);
+
+    private static final String RESOURCES = "/" + 
ProFtpdTest.class.getPackage().getName().replace('.', '/');
+
+    @Container
+    static GenericContainer<?> server = new GenericContainer<>(
+            new ImageFromDockerfile().withDockerfileFromBuilder(builder -> 
builder.from("instantlinux/proftpd:latest") //
+                    // Get SFTP logs on stderr
+                    .run("sed -i -e 's:^WtmpLog:AllowLogSymLinks 
on\\nSystemLog /dev/stderr\\nSFTPLog /dev/stderr\\nWtmpLog:' 
/etc/proftpd/proftpd.conf") //
+                    .build())) //
+            .withEnv("PASV_ADDRESS", "127.0.0.1") //
+            .withEnv("FTPUSER_NAME", "test") //
+            .withEnv("SFTP_ENABLE", "on") //
+            // Set it up for password auth
+            
.withCopyFileToContainer(MountableFile.forClasspathResource(RESOURCES + 
"/proftpd.pwd"),
+                    "/run/secrets/ftp-user-password-secret")
+            .withExposedPorts(2222) //
+            .withLogConsumer(new Slf4jLogConsumer(LOG));
+
+    private SshClient client;
+
+    @BeforeEach
+    void setUp() throws Exception {
+        client = SshClient.setUpDefaultClient();
+        client.setServerKeyVerifier((s, a, k) -> true);
+        client.start();
+    }
+
+    @AfterEach
+    void teardown() {
+        if (client != null) {
+            client.stop();
+        }
+    }
+
+    @Test // GH-891
+    void ignoreMesssage() throws Exception {
+        CoreModuleProperties.IGNORE_MESSAGE_FREQUENCY.set(client, 20L);
+        CoreModuleProperties.IGNORE_MESSAGE_VARIANCE.set(client, 0);
+        CoreModuleProperties.IGNORE_MESSAGE_SIZE.set(client, 16);
+        byte[] expected = new byte[8 * 1024 * 1024];
+
+        Factory<? extends Random> factory = client.getRandomFactory();
+        Random rnd = factory.create();
+        rnd.fill(expected);
+
+        try (ClientSession sshSession = client.connect("test", 
server.getHost(), server.getMappedPort(2222)).verify()
+                .getClientSession()) {
+            sshSession.addPasswordIdentity("test");
+            sshSession.auth().verify(2000);
+            try (SftpClient sftp = 
SftpClientFactory.instance().createSftpClient(sshSession)) {
+                // Copy some data onto the server
+                try (InputStream in = new ByteArrayInputStream(expected);
+                     OutputStream out = sftp.write("/home/test/file.bin", 
SftpClient.OpenMode.Create,
+                             SftpClient.OpenMode.Write)) {
+                    IoUtils.copy(in, out);
+                }
+                byte[] actual;
+                try (InputStream in = sftp.read("/home/test/file.bin");
+                     ByteArrayOutputStream buf = new 
ByteArrayOutputStream(expected.length)) {
+                    byte[] data = new byte[4096];
+                    for (int n = 0; n >= 0;) {
+                        n = in.read(data, 0, data.length);
+                        if (n > 0) {
+                            buf.write(data, 0, n);
+                        }
+                    }
+                    actual = buf.toByteArray();
+                }
+                assertArrayEquals(expected, actual);
+            }
+        }
+    }
+
+}
diff --git 
a/sshd-sftp/src/test/resources/org/apache/sshd/sftp/client/proftpd.pwd 
b/sshd-sftp/src/test/resources/org/apache/sshd/sftp/client/proftpd.pwd
new file mode 100644
index 000000000..a34ce4506
--- /dev/null
+++ b/sshd-sftp/src/test/resources/org/apache/sshd/sftp/client/proftpd.pwd
@@ -0,0 +1 @@
+$6$Q7V2n3q2GRVv5YeQ$/AhLu07H76Asojy7bxGXMY1caKLAbp5Vt82LOZYMkD/8uDzyMAEXwk0c1Bdz1DkBsk2Vh/9SF130mOPavRGMo.
\ No newline at end of file

Reply via email to