Go SSH 服务器通过 SCP 接受文件传输

Go SSH server accepting file transfers via SCP

我想用 Go 创建一个服务器进程,它可以通过 scp 接收或发送文件,比如:

scp -P 2200 -i ssh/tester_rsa  foo@localhost:/test output.txt

scp -P 2200 -i ssh/tester_rsa  input.txt foo@localhost:/test

this gist 的帮助下,我已经能够创建一个交互式 ssh 服务器,它能够通过

访问 connections/commands
ssh -i ssh/tester_rsa foo@localhost -p 2200 'somecommand'

并且工作得很好。

我了解到有效负载包含第一个显示的 scp 请求的 "scp -f /test"。我被困在如何 return 来自服务器的实际内容上,我尝试用字符串回复(就像普通的 ssh 回复一样)但是我没有得到任何文件。

这是我的 scp -vv

的输出
...
debug2: callback done
debug2: channel 0: open confirm rwindow 2097152 rmax 32768
debug2: channel_input_status_confirm: type 99 id 0
debug2: exec request accepted on channel 0
debug1: client_input_channel_req: channel 0 rtype exit-status reply 0
debug2: channel 0: rcvd close
debug2: channel 0: output open -> drain
debug2: channel 0: close_read
...

当我尝试使用适当的服务器时,我得到以下几行

...
debug2: exec request accepted on channel 0
debug2: channel 0: rcvd ext data 43
Sending file modes: C0644 98 output.txt
debug2: channel 0: written 43 to efd 7
Sink: C0644 98 output.txt
output.txt           
debug1: client_input_channel_req: channel 0 rtype exit-status reply 0
debug1: client_input_channel_req: channel 0 rtype eow@openssh.com     reply 0
debug2: channel 0: rcvd eow
debug2: channel 0: close_read
...

所以我想我在 Go 中缺少一些协议/流参数或命令。

只有Go代码的片段,因为项目很大,不容易复制粘贴到这里:

func (a *Shell) handleExec(connection ssh.Channel, payload []byte) {
...
for _, c := range a.ExecCmds {
    if matchCmds(str, c.Cmd) {
        connection.Write([]byte("Here comes the file content")
    }
}
...

我知道该代码不适用于 scp,但它适用于普通 ssh。我不知道如何处理连接,也不知道 return 究竟要做什么才能完成正确的文件传输。

谷歌搜索了一段时间后,我找到了 this question on stack overflow,这几乎是一样的,但没有得到完全的回答,因为我正在寻找

 runYourCommand(msg.Command, ch)

我猜其中包含逻辑:)

更新:我不再寻找外部资源,我从 this java snippet

那里得到了更多信息

看起来他们正在模拟 scp 协议,如果有人帮助我将其翻译成 Go,我会非常高兴:

  StringBuilder params = new StringBuilder("C0").append(perms);
  params.append(" ").append(data.length).append(" ");
  params.append(remoteFileName).append("\n");

  out.write(params.toString().getBytes());
  out.flush();

  if (!waitForInputStream(logger, in)) {
     cleanup(logger, out, channel);
     logger.log(Level.SEVERE, "Error before writing SCP bytes");
     return UNKNOWN_ERROR;           /* TODO: Improve */
  }

  out.write(data);
  out.write(new byte[]{0}, 0, 1);
  out.flush();

到目前为止我试过了,但没有帮助

o:="Some content of a faked file"
connection.Write([]byte("C0644 "+string(len(o))+"test.txt\n"))
connection.Write([]byte(o))
connection.Write([]byte{0,0,1})

澄清一下:我不想提供真实文件,而是通过 scp 模拟文件传输(使用字符串作为文件内容)。

提前致谢!

好的,我自己找到了解决方案,虽然很小但是使代码有效:

prep := "C0644 " + strconv.Itoa(len(o)) + " test.txt\n"
connection.Write([]byte(prep))
connection.Write([]byte(o))
connection.Write([]byte("\x00"))

我在长度后的准备变量中遗漏了一个 space,长度没有正确转换,关闭的“\x00”以这种方式工作得更好。现在我可以收到一个包含内容的假文件:)