func handleChannel(ch ssh.NewChannel) { if ch.ChannelType() != "session" { log.Printf("[ERROR] unknown channel type %q", ch.ChannelType()) ch.Reject(ssh.UnknownChannelType, "unknown channel type") return } channel, requests, err := ch.Accept() if err != nil { log.Printf("[ERROR] could not accept channel %q", err) } defer channel.Close() var master, slave *os.File defer func() { if master != nil { master.Close() } if slave != nil { slave.Close() } }() var wg sync.WaitGroup wg.Add(1) go func(in <-chan *ssh.Request) { for req := range in { log.Printf("[INFO] got request %q", req.Type) switch req.Type { case "shell": if len(req.Payload) > 0 { // do not accept any extra commands for shell req.Reply(false, nil) return } req.Reply(true, nil) case "pty-req": pty, tty, err := pty.Open() if err != nil { log.Printf("[ERROR] failed to open a pty %q", err) return } master, slave = pty, tty wg.Done() // FIXME handle pty request req.Reply(true, nil) case "window-change": // FIXME handle window resize req.Reply(true, nil) } } }(requests) wg.Wait() go io.Copy(channel, master) go io.Copy(master, channel) s := bufio.NewScanner(slave) for { prompt(slave) if !s.Scan() { break } line := s.Text() // clear the terminal and continue if line == "cls" { fmt.Fprintf(slave, "\033[2J") fmt.Fprintf(slave, "\033[1;1H") continue } tokens := strings.Split(line, " ") cmd := exec.Command("docker", tokens...) cmd.Stdin = slave cmd.Stdout = slave cmd.Stderr = slave cmd.SysProcAttr = &syscall.SysProcAttr{Setctty: true, Setsid: true} log.Println("command: %#v", cmd) if err := cmd.Start(); err != nil { fmt.Fprintf(master, "invalid docker cmd %q\n", line) } if err := cmd.Wait(); err != nil { fmt.Fprintf(master, "cmd %q failed\n", line) } fmt.Fprint(slave, "\r\n") } }
func (chi *ChanInfo) Serve(conn ssh.Conn, newChan ssh.NewChannel) (err error) { log.Info("new channel: %s (len: %d)", newChan.ChannelType(), len(newChan.ExtraData())) err = chi.onType(newChan.ChannelType(), newChan.ExtraData()) if err != nil { newChan.Reject(ssh.ResourceShortage, err.Error()) log.Error("reject channel: %s", err.Error()) return } chout, outreqs, err := conn.OpenChannel( newChan.ChannelType(), newChan.ExtraData()) if err != nil { newChan.Reject(ssh.UnknownChannelType, err.Error()) log.Error("reject channel: %s", err.Error()) return } log.Debug("open channel ok.") chin, inreqs, err := newChan.Accept() if err != nil { log.Error("could not accept channel.") return } log.Debug("accept channel ok.") go chi.serveReqs(chin, outreqs) go chi.serveReqs(chout, inreqs) _, ok := <-chi.ch if !ok { return } switch chi.Type { case "local", "remote": go MultiCopyClose(chin, chout, &DebugStream{"out"}) go MultiCopyClose(chout, chin, &DebugStream{"in"}) case "sshagent": go MultiCopyClose(chin, chout, &DebugStream{"out"}) go MultiCopyClose(chout, chin, &DebugStream{"in"}) case "shell": l, err := chi.prepareFile("") if err != nil { return err } go MultiCopyClose(chin, chout, l.CreateSubLogger(byte(0x01))) go MultiCopyClose(chout, chin, l.CreateSubLogger(byte(0x02))) case "exec": l, err := chi.prepareFile(strings.Join(chi.ExecCmds, "\r")) if err != nil { return err } go MultiCopyClose(chin, chout, l.CreateSubLogger(byte(0x01))) go MultiCopyClose(chout, chin, l.CreateSubLogger(byte(0x02))) case "scpto": go MultiCopyClose(chin, chout, CreateScpStream(chi)) go MultiCopyClose(chout, chin) case "scpfrom": go MultiCopyClose(chin, chout) go MultiCopyClose(chout, chin, CreateScpStream(chi)) default: log.Warning("redirect before setup") chin.Close() chout.Close() } return }