dongzhong1891
2017-06-30 19:19 阅读 127
已采纳

Go SSH服务器通过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("
");

  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
"))
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条回答 默认 最新

  • 已采纳
    dongwuli5105 dongwuli5105 2017-07-02 13:41

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

    prep := "C0644 " + strconv.Itoa(len(o)) + " test.txt
    "
    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 :)

    点赞 评论 复制链接分享

相关推荐