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

huangapple go评论92阅读模式
英文:

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

这个代码片段的帮助下,我已经能够创建一个交互式的SSH服务器,可以通过以下方式接受连接/命令:

ssh -i ssh/tester_rsa foo@localhost -p 2200 'somecommand'

并且运行良好。

我了解到第一个显示的scp请求的有效负载包含"scp -f /test"。我在如何从服务器返回实际内容方面遇到了困难,我尝试通过回复一个字符串(与正常的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来说很好用。我不知道如何处理连接,以及要返回什么内容才能正确进行文件传输。

在Google上搜索了一段时间后,我找到了stackoverflow上的这个问题,它几乎相同,但没有完全回答,因为我正好在寻找这部分内容:

runYourCommand(msg.Command, ch)

我猜这部分包含了逻辑 Go SSH服务器通过SCP接受文件传输。

更新:我不再寻找外部资源,我从这个Java代码片段中获得了更多的输入。

看起来他们在模拟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})

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

提前感谢!

英文:

I would like to create a server process with Go which can receive or send files via scp, like:

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

or

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

With help of this gist I was already able to create an interactive ssh server which is able to accpet connections/commands via

ssh -i ssh/tester_rsa foo@localhost -p 2200 'somecommand'

and works nicely.

I understood that the payload contains "scp -f /test" for the first shown scp request. I am stuck in how to return actual content from the server, I tried in replying with a string (as with a normal ssh reply) but I get no file back.

Here my output of 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
...

When I am trying with a propper server I get following lines

...
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
...

So I guess I am missing some protocol / flow parameters or commands in Go.

Only the snippet of Go Code, as project is huge and is not easy to copy and paste here:

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")
	}
}
...

I know that the code is not right for scp, but it works fine for normal ssh. I don't know how to handle the connection, and what exactly to return so that a correct file transfer is done.

Googling for a while led me to this question on stack overflow which is nearly the same but got not completely answered, as I am exactly looking for the part

 runYourCommand(msg.Command, ch)

which I guess contains the logic Go SSH服务器通过SCP接受文件传输。

Update: I am looking not for external ressources any more, I got some more input from this java snippet

It looks like they are emulating the scp protocol and I would perfectly be fine to have someone helping me to translate it to 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();

I tried this so far, but didn't help

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})

To clarify: I would not like to serve a real file, rather then Go simulating a file delivery over scp (using a string as the content of the file).

Thanks in advance!

答案1

得分: 0

好的,下面是翻译好的内容:

好的,我找到了解决方案,虽然很小,但是让代码正常工作了:

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

在长度后面的prep变量中,我漏掉了一个空格,导致长度没有正确转换,而且在这种方式下,闭合的"\x00"更好地起作用。现在我能够接收一个带有内容的虚拟文件 Go SSH服务器通过SCP接受文件传输。

英文:

Ok, found myself the solution, small but that made the code work:

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

I was missing a space in prep variable after length, the length was not converted correctly and the closing "\x00" is working better this way. Now I am able to receive a fake file with content Go SSH服务器通过SCP接受文件传输。

huangapple
  • 本文由 发表于 2017年7月1日 03:19:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/44853505.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定