英文:
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)
我猜这部分包含了逻辑
更新:我不再寻找外部资源,我从这个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
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"更好地起作用。现在我能够接收一个带有内容的虚拟文件
英文:
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论