I have written a small SSH-Server in golang with the crypto/ssh package.
It supports returning an interactive shell and immidiate command execution.
Here is a minimal example of the server:
package main import ( "fmt" "io/ioutil" "log" "net" "os/exec" "golang.org/x/crypto/ssh" ) func main() { c := &ssh.ServerConfig{ PasswordCallback: func(c ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) { if c.User() == "foo" && string(pass) == "bar" { return nil, nil } return nil, fmt.Errorf("password rejected for %q", c.User()) }, } keyBytes, _ := ioutil.ReadFile("key") key, _ := ssh.ParsePrivateKey(keyBytes) c.AddHostKey(key) listener, _ := net.Listen("tcp", "0.0.0.0:2200") for { tcpConn, _ := listener.Accept() _, chans, reqs, _ := ssh.NewServerConn(tcpConn, c) go ssh.DiscardRequests(reqs) go handleChannels(chans) } } func handleChannels(chans <-chan ssh.NewChannel) { for newChannel := range chans { go handleChannel(newChannel) } } func handleChannel(newChannel ssh.NewChannel) { channel, requests, _ := newChannel.Accept() for req := range requests { switch req.Type { case "shell": go handleShell(channel) case "exec": go handleExec(channel, req) } } } func handleShell(c ssh.Channel) {} func handleExec(c ssh.Channel, r *ssh.Request) { cmdString, args, _ := parseCommand(r.Payload) log.Printf("exec: %s ", cmdString) for i := range args { log.Printf("arg %d: %s ", i, args[i]) } cmd := exec.Command(cmdString, args...) cmd.Run() } func parseCommand(b []byte) (string, []string, error) { cmdString := strings.TrimSpace(string(b)) cmdArray := strings.Split(cmdString, " ") cmd := strings.Trim(cmdArray[0], " ") args := cmdArray[1:] return cmd, args, nil }
If I run the server and execute scp as follows:
scp -P 2200 test.file foo@localhost:~/
the handleExec function is called.
The output of the cmdString shows:
2015/11/22 17:49:14 exec: scp 2015/11/22 17:49:14 arg 0: -t 2015/11/22 17:49:14 arg 1: ~/
But how can I implement the handleExec function to actually save the file/dir I passed via scp?