hierynomus / sshj

ssh, scp and sftp for java
Apache License 2.0
2.51k stars 601 forks source link

SCP upload to Windows 11 Open SSH fails with no such file or directory error #929

Open robertpatrick opened 9 months ago

robertpatrick commented 9 months ago

We are using SSHJ to add remoting capabilities to our app. We are currently testing cross-platform capabilities where the client is running on Linux connecting to a Windows 11 Enterprise server using the OpenSSH server provided by the Windows distribution. SSH is working fine. SCP download is working fine. SCP upload is failing with:

Exception in thread "main" net.schmizz.sshj.xfer.scp.SCPRemoteException: Remote SCP command had error: scp: 'c:\Temp\model.yaml': No such file or directory
        at net.schmizz.sshj.xfer.scp.SCPEngine.check(SCPEngine.java:73)
        at net.schmizz.sshj.xfer.scp.SCPEngine.sendMessage(SCPEngine.java:133)
        at net.schmizz.sshj.xfer.scp.SCPUploadClient.sendFile(SCPUploadClient.java:105)
        at net.schmizz.sshj.xfer.scp.SCPUploadClient.process(SCPUploadClient.java:84)
        at net.schmizz.sshj.xfer.scp.SCPUploadClient.startCopy(SCPUploadClient.java:76)
        at net.schmizz.sshj.xfer.scp.SCPUploadClient.copy(SCPUploadClient.java:54)
        at net.schmizz.sshj.xfer.scp.SCPUploadClient.copy(SCPUploadClient.java:47)
        at net.schmizz.sshj.xfer.scp.SCPUploadClient.copy(SCPUploadClient.java:43)
        at net.schmizz.sshj.xfer.scp.SCPFileTransfer.upload(SCPFileTransfer.java:98)
        at net.schmizz.sshj.xfer.scp.SCPFileTransfer.upload(SCPFileTransfer.java:61)
        at net.schmizz.sshj.xfer.scp.SCPFileTransfer.upload(SCPFileTransfer.java:55)
        at com.oracle.test.SSHTest.main(SSHTest.java:36)

The odd thing is that scp from the command-line works fine.

[rpatrick@rpatrick-1 ssh-test]$ scp ./model.yaml rpatrick@<internal-hostname-redacted>:c:\\Temp\\model.yaml
model.yaml                                                                                                                                                                                100%  486   238.1KB/s   00:00
[rpatrick@rpatrick-1 ssh-test]$

My code is just a little sample that I created trying to debug this problem. As you can see, it isn't doing anything fancy and is allowing SSHJ to get the default username and SSH private key (from ~/.ssh/id_rsa).

package com.oracle.test;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.connection.channel.direct.Session.Command;
import net.schmizz.sshj.xfer.scp.SCPFileTransfer;

public class SSHTest {
    public static void main(String[] args) throws Exception {
        try (SSHClient sshClient = new SSHClient()) {
            sshClient.loadKnownHosts();
            sshClient.connect("<internal-hostname-redacted>");
            Session session = null;
            try {
                sshClient.authPublickey(System.getProperty("user.name"));
                session = sshClient.startSession();
                final Command cmd = session.exec("wmic os get caption");
                System.out.print(IOUtils.readFully(cmd.getInputStream()));
                cmd.join(5, TimeUnit.SECONDS);
                System.out.println("\n** exit status: " + cmd.getExitStatus() + "\n\n");

                SCPFileTransfer scpFileTransfer = sshClient.newSCPFileTransfer();
                scpFileTransfer.download("c:\\Temp\\ssh\\model.yaml", "./model.yaml");
                System.out.println("Downloaded file\n\n");

                scpFileTransfer = sshClient.newSCPFileTransfer();
                scpFileTransfer.upload("./model.yaml", "c:\\Temp\\model.yaml");
                System.out.println("Uploaded file\n\n");
            } finally {
                if (session != null) {
                    try { session.close(); } catch (IOException ex) { /* ignore */ }
                }
                sshClient.disconnect();
            }
        }
    }
}

In the output from this program, I see the following before the stack trace:

Caption
Microsoft Windows 11 Enterprise

** exit status: 0

Downloaded file

Exception in thread "main" net.schmizz.sshj.xfer.scp.SCPRemoteException: Remote SCP command had error: scp: 'c:\Temp\model.yaml': No such file or directory
...
robertpatrick commented 9 months ago

Here is the relevant snippet from the OpenSSH server log:

7588 2024-02-16 08:20:33.372 Starting session: command for oradev\\\\rpatrick from 10.89.201.18 port 57849 id 0
7588 2024-02-16 08:20:33.373 debug2: fd 10 setting O_NONBLOCK
7588 2024-02-16 08:20:33.373 debug2: fd 11 setting O_NONBLOCK
7588 2024-02-16 08:20:33.373 debug2: fd 12 setting O_NONBLOCK
7588 2024-02-16 08:20:33.373 debug2: fd 13 setting O_NONBLOCK
7588 2024-02-16 08:20:33.373 debug2: fd 14 setting O_NONBLOCK
7588 2024-02-16 08:20:33.373 debug2: fd 15 setting O_NONBLOCK
7588 2024-02-16 08:20:33.373 debug3: shell: "c:\\\\windows\\\\system32\\\\cmd.exe"
7588 2024-02-16 08:20:33.373 debug3: shell_option: /c
7588 2024-02-16 08:20:33.374 debug3: exec_command: scp.exe -t -r -p 'c://Temp//'
7588 2024-02-16 08:20:33.374 debug3: arg escape option: TRUE
7588 2024-02-16 08:20:33.374 debug3: spawn_argv[0]: "c:\\\\windows\\\\system32\\\\cmd.exe" /c "scp.exe -t -r -p 'c://Temp//'"
7588 2024-02-16 08:20:33.374 debug3: spawning "c:\\\\windows\\\\system32\\\\cmd.exe" /c "scp.exe -t -r -p 'c://Temp//'" as subprocess
7588 2024-02-16 08:20:33.383 debug3: fd 12 is O_NONBLOCK
7588 2024-02-16 08:20:33.383 debug3: fd 11 is O_NONBLOCK
7588 2024-02-16 08:20:33.383 debug3: fd 14 is O_NONBLOCK
7588 2024-02-16 08:20:33.383 debug3: send packet: type 99
7588 2024-02-16 08:20:33.707 debug3: receive packet: type 97
7588 2024-02-16 08:20:33.707 debug2: channel 0: rcvd close
7588 2024-02-16 08:20:33.707 debug2: channel 0: output open -> drain
7588 2024-02-16 08:20:33.707 debug2: chan_shutdown_read: channel 0: (i0 o1 sock -1 wfd 12 efd 14 [read])
7588 2024-02-16 08:20:33.707 debug1: chan_shutdown_extended_read: channel 0: (i0 o1 sock -1 wfd -1 efd 14 [read])
7588 2024-02-16 08:20:33.707 debug2: channel 0: input open -> closed
7588 2024-02-16 08:20:33.707 debug3: channel 0: will not send data after close
7588 2024-02-16 08:20:33.707 debug2: channel 0: obuf empty
7588 2024-02-16 08:20:33.707 debug2: chan_shutdown_write: channel 0: (i3 o1 sock -1 wfd 11 efd -1 [closed])
7588 2024-02-16 08:20:33.707 debug2: channel 0: output drain -> closed
7588 2024-02-16 08:20:33.707 debug2: channel 0: almost dead
7588 2024-02-16 08:20:33.707 debug2: channel 0: gc: notify user
7588 2024-02-16 08:20:33.707 debug1: session_by_channel: session 0 channel 0
7588 2024-02-16 08:20:33.708 debug1: session_close_by_channel: channel 0 child 2520
7588 2024-02-16 08:20:33.708 debug1: session_close_by_channel: channel 0: has child, ttyfd -1
7588 2024-02-16 08:20:33.718 debug1: Received SIGCHLD.
7588 2024-02-16 08:20:33.718 debug1: session_by_pid: pid 2520
7588 2024-02-16 08:20:33.718 debug1: session_exit_message: session 0 channel 0 pid 2520
7588 2024-02-16 08:20:33.718 debug2: channel 0: request exit-status confirm 0
7588 2024-02-16 08:20:33.718 debug3: send packet: type 98
7588 2024-02-16 08:20:33.719 debug1: session_exit_message: release channel 0
7588 2024-02-16 08:20:33.719 debug2: channel 0: send close
7588 2024-02-16 08:20:33.719 debug3: send packet: type 97
7588 2024-02-16 08:20:33.719 debug2: channel 0: is dead
7588 2024-02-16 08:20:33.719 debug2: channel 0: gc: notify user
7588 2024-02-16 08:20:33.719 debug1: session_by_channel: session 0 channel 0
7588 2024-02-16 08:20:33.719 debug1: session_close_by_channel: channel 0 child 0
7588 2024-02-16 08:20:33.719 Close session: user oradev\\\\rpatrick from 10.89.201.18 port 57849 id 0
robertpatrick commented 9 months ago

It appears that the escaping/path conversion is different between download and upload. Notice how the Windows path has // in the failing upload case and / in the successful download case. My input paths in both cases use \\ separators. Maybe this is the cause of the "No such file or directory" error?

Upload snippet from SSH server log:

4276 2024-02-16 09:31:16.488 debug3: shell: "c:\\\\windows\\\\system32\\\\cmd.exe"
4276 2024-02-16 09:31:16.488 debug3: shell_option: /c
4276 2024-02-16 09:31:16.488 debug3: exec_command: scp.exe -t -r -p 'c://Temp'
4276 2024-02-16 09:31:16.488 debug3: arg escape option: TRUE
4276 2024-02-16 09:31:16.488 debug3: spawn_argv[0]: "c:\\\\windows\\\\system32\\\\cmd.exe" /c "scp.exe -t -r -p 'c://Temp'"
4276 2024-02-16 09:31:16.488 debug3: spawning "c:\\\\windows\\\\system32\\\\cmd.exe" /c "scp.exe -t -r -p 'c://Temp'" as subprocess

Download snippet from SSH server log:

4724 2024-02-16 09:26:34.519 debug3: shell: "c:\\\\windows\\\\system32\\\\cmd.exe"
4724 2024-02-16 09:26:34.519 debug3: shell_option: /c
4724 2024-02-16 09:26:34.519 debug3: exec_command: scp.exe -f -q -p -r c:/Temp/ssh/model.yaml
4724 2024-02-16 09:26:34.519 debug3: arg escape option: TRUE
4724 2024-02-16 09:26:34.519 debug3: spawn_argv[0]: "c:\\\\windows\\\\system32\\\\cmd.exe" /c "scp.exe -f -q -p -r c:/Temp/ssh/model.yaml"
4724 2024-02-16 09:26:34.519 debug3: spawning "c:\\\\windows\\\\system32\\\\cmd.exe" /c "scp.exe -f -q -p -r c:/Temp/ssh/model.yaml" as subprocess

I'm not sure where exactly the code is that is converting the upload path. I tried changing it in a couple of different places and rebuilding the JAR as a test but it seems to have no effect.

robertpatrick commented 9 months ago

@hierynomus Any thoughts on this?

anantharaman93 commented 5 months ago

I am also facing the same issue. Is there any solution for the same ? @robertpatrick @hierynomus

robertpatrick commented 5 months ago

Nothing so far. I have just documented this as a limitation for now. It seems like none of the SSHJ committers are interested.

RicCanuto commented 3 weeks ago

I'm facing the same issue. Any news on this topic?

RicCanuto commented 3 weeks ago

Nothing so far. I have just documented this as a limitation for now. It seems like none of the SSHJ committers are interested.

Did you found any workaround? Thank you