Skip to content

Commit c65f0dd

Browse files
committed
Refactor request type handlers into a stateful struct.
1 parent 3d59dc3 commit c65f0dd

File tree

2 files changed

+217
-157
lines changed

2 files changed

+217
-157
lines changed

execd.go

Lines changed: 1 addition & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,17 @@ import (
77
"errors"
88
"flag"
99
"fmt"
10-
"io"
1110
"io/ioutil"
1211
"log"
1312
"net"
1413
"os"
1514
"os/exec"
1615
"path/filepath"
1716
"strings"
18-
"sync"
1917
"syscall"
2018

21-
"code.google.com/p/go.crypto/ssh"
2219
"github.com/flynn/go-shlex"
23-
"github.com/kr/pty"
20+
"golang.org/x/crypto/ssh"
2421
)
2522

2623
var host = flag.String("h", "", "host ip to listen on")
@@ -50,27 +47,6 @@ func exitStatus(err error) (exitStatusMsg, error) {
5047
return exitStatusMsg{0}, nil
5148
}
5249

53-
func attachShell(cmd *exec.Cmd, stdout io.Writer, stdin io.Reader) (*os.File, *sync.WaitGroup, error) {
54-
var wg sync.WaitGroup
55-
wg.Add(2)
56-
57-
// Note that pty merges stdout and stderr.
58-
cmdPty, err := pty.Start(cmd)
59-
if err != nil {
60-
return nil, nil, err
61-
}
62-
go func() {
63-
io.Copy(stdout, cmdPty)
64-
wg.Done()
65-
}()
66-
go func() {
67-
io.Copy(cmdPty, stdin)
68-
wg.Done()
69-
}()
70-
71-
return cmdPty, &wg, nil
72-
}
73-
7450
func addKey(conf *ssh.ServerConfig, block *pem.Block) (err error) {
7551
var key interface{}
7652
switch block.Type {
@@ -223,135 +199,3 @@ func handleConn(conn net.Conn, conf *ssh.ServerConfig, execHandler []string) {
223199
go handleChannel(sshConn, ch, execHandler)
224200
}
225201
}
226-
227-
func handleChannel(conn *ssh.ServerConn, newChan ssh.NewChannel, execHandler []string) {
228-
ch, reqs, err := newChan.Accept()
229-
if err != nil {
230-
log.Println("newChan.Accept failed:", err)
231-
return
232-
}
233-
234-
assert := func(at string, err error) bool {
235-
if err != nil {
236-
log.Printf("%s failed: %s", at, err)
237-
ch.Stderr().Write([]byte("Internal error.\n"))
238-
return true
239-
}
240-
return false
241-
}
242-
243-
var stdout, stderr io.Writer
244-
if *debug {
245-
stdout = io.MultiWriter(ch, os.Stdout)
246-
stderr = io.MultiWriter(ch.Stderr(), os.Stdout)
247-
} else {
248-
stdout = ch
249-
stderr = ch.Stderr()
250-
}
251-
252-
var ptyShell *os.File
253-
254-
for req := range reqs {
255-
switch req.Type {
256-
case "exec":
257-
if req.WantReply {
258-
req.Reply(true, nil)
259-
}
260-
261-
cmdline := string(req.Payload[4:])
262-
var cmd *exec.Cmd
263-
if *shell {
264-
shellcmd := flag.Arg(1) + " " + cmdline
265-
cmd = exec.Command(os.Getenv("SHELL"), "-c", shellcmd)
266-
} else {
267-
cmdargs, err := shlex.Split(cmdline)
268-
if assert("shlex.Split", err) {
269-
ch.Close()
270-
return
271-
}
272-
cmd = exec.Command(execHandler[0], append(execHandler[1:], cmdargs...)...)
273-
}
274-
if *env {
275-
cmd.Env = os.Environ()
276-
} else {
277-
cmd.Env = []string{}
278-
}
279-
if conn.Permissions != nil {
280-
// Using Permissions.Extensions as a way to get state from PublicKeyCallback
281-
if conn.Permissions.Extensions["environ"] != "" {
282-
cmd.Env = append(cmd.Env, strings.Split(conn.Permissions.Extensions["environ"], "\n")...)
283-
}
284-
cmd.Env = append(cmd.Env, "USER="+conn.Permissions.Extensions["user"])
285-
}
286-
cmd.Env = append(cmd.Env, "SSH_ORIGINAL_COMMAND="+cmdline)
287-
288-
// cmd.Wait closes the stdin when it's done, so we need to proxy it through a pipe
289-
stdinPipe, err := cmd.StdinPipe()
290-
if assert("cmd.StdinPipe", err) {
291-
ch.Close()
292-
return
293-
}
294-
go io.Copy(stdinPipe, ch)
295-
296-
cmd.Stdout = stdout
297-
cmd.Stderr = stderr
298-
299-
go func() {
300-
status, err := exitStatus(cmd.Run())
301-
if !assert("exec run", err) {
302-
_, err := ch.SendRequest("exit-status", false, ssh.Marshal(&status))
303-
assert("exec exit", err)
304-
}
305-
ch.Close()
306-
}()
307-
case "pty-req":
308-
width, height, okSize := parsePtyRequest(req.Payload)
309-
310-
var cmd *exec.Cmd
311-
if *shell {
312-
cmd = exec.Command(os.Getenv("SHELL"))
313-
} else {
314-
cmd = exec.Command(execHandler[0], execHandler[1:]...)
315-
}
316-
if *env {
317-
cmd.Env = os.Environ()
318-
} else {
319-
cmd.Env = []string{}
320-
}
321-
if conn.Permissions != nil {
322-
// Using Permissions.Extensions as a way to get state from PublicKeyCallback
323-
if conn.Permissions.Extensions["environ"] != "" {
324-
cmd.Env = append(cmd.Env, strings.Split(conn.Permissions.Extensions["environ"], "\n")...)
325-
}
326-
cmd.Env = append(cmd.Env, "USER="+conn.Permissions.Extensions["user"])
327-
}
328-
ptyShell, _, err := attachShell(cmd, stdout, ch)
329-
if assert("attachShell", err) {
330-
ch.Close()
331-
return
332-
}
333-
if okSize {
334-
setWinsize(ptyShell.Fd(), width, height)
335-
req.Reply(true, nil)
336-
}
337-
338-
go func() {
339-
status, err := exitStatus(cmd.Wait())
340-
if !assert("pty run", err) {
341-
_, err := ch.SendRequest("exit-status", false, ssh.Marshal(&status))
342-
assert("pty exit", err)
343-
}
344-
ch.Close()
345-
}()
346-
case "window-change":
347-
width, height, okSize := parsePtyRequest(req.Payload)
348-
if okSize {
349-
setWinsize(ptyShell.Fd(), width, height)
350-
}
351-
}
352-
353-
if req.WantReply {
354-
req.Reply(true, nil)
355-
}
356-
}
357-
}

0 commit comments

Comments
 (0)