From 8b19b11c3f60125de986262c0b06e492b3302b91 Mon Sep 17 00:00:00 2001 From: elfgzp <741424975@qq.com> Date: Tue, 17 Dec 2019 00:46:22 +0800 Subject: [PATCH 1/4] Add Pty.Termmodes --- session_test.go | 9 +++++++- ssh.go | 6 +++--- util.go | 57 +++++++++++++++++++++++++++++++++++++------------ 3 files changed, 54 insertions(+), 18 deletions(-) diff --git a/session_test.go b/session_test.go index 786a661..0d509a7 100644 --- a/session_test.go +++ b/session_test.go @@ -199,6 +199,9 @@ func TestPty(t *testing.T) { term := "xterm" winWidth := 40 winHeight := 80 + termModes := make(gossh.TerminalModes) + ttyOPOSPEED := uint32(38400) + termModes[gossh.TTY_OP_OSPEED] = ttyOPOSPEED done := make(chan bool) session, _, cleanup := newTestSession(t, &Server{ Handler: func(s Session) { @@ -215,11 +218,15 @@ func TestPty(t *testing.T) { if ptyReq.Window.Height != winHeight { t.Fatalf("expected window height %#v but got %#v", winHeight, ptyReq.Window.Height) } + mode := ptyReq.Termmodes[gossh.TTY_OP_OSPEED] + if ptyReq.Termmodes[gossh.TTY_OP_OSPEED] != ttyOPOSPEED { + t.Fatalf("expected mode %#v but got %#v", ttyOPOSPEED, mode) + } close(done) }, }, nil) defer cleanup() - if err := session.RequestPty(term, winHeight, winWidth, gossh.TerminalModes{}); err != nil { + if err := session.RequestPty(term, winHeight, winWidth, termModes); err != nil { t.Fatalf("expected nil but got %v", err) } if err := session.Shell(); err != nil { diff --git a/ssh.go b/ssh.go index 9673ac3..f3421ee 100644 --- a/ssh.go +++ b/ssh.go @@ -72,9 +72,9 @@ type Window struct { // Pty represents a PTY request and configuration. type Pty struct { - Term string - Window Window - // HELP WANTED: terminal modes! + Term string + Window Window + Termmodes gossh.TerminalModes } // Serve accepts incoming SSH connections on the listener l, creating a new diff --git a/util.go b/util.go index 015a44e..2c26d5a 100644 --- a/util.go +++ b/util.go @@ -8,6 +8,19 @@ import ( "golang.org/x/crypto/ssh" ) +type ptyRequestMsg struct { + Term string + Columns uint32 + Rows uint32 + Width uint32 + Height uint32 + Modelist string +} + +const ( + ttyOPEND = 0 +) + func generateSigner() (ssh.Signer, error) { key, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { @@ -17,26 +30,42 @@ func generateSigner() (ssh.Signer, error) { } func parsePtyRequest(s []byte) (pty Pty, ok bool) { - term, s, ok := parseString(s) - if !ok { - return - } - width32, s, ok := parseUint32(s) - if !ok { - return + reqMsg := &ptyRequestMsg{} + err := ssh.Unmarshal(s, reqMsg) + if err != nil { + return Pty{}, false } - height32, _, ok := parseUint32(s) - if !ok { - return + + modes := []byte(reqMsg.Modelist) + + mode := struct { + Key uint8 + Val uint32 + }{} + + termModes := make(ssh.TerminalModes, 0) + for { + if len(modes) < 1 || modes[0] == ttyOPEND || len(modes) < 5 { + break + } + b := modes[:5] + err = ssh.Unmarshal(b, &mode) + if err != nil { + return Pty{}, false + } + termModes[mode.Key] = mode.Val + modes = modes[6:] } + pty = Pty{ - Term: term, + Term: reqMsg.Term, Window: Window{ - Width: int(width32), - Height: int(height32), + Width: int(reqMsg.Columns), + Height: int(reqMsg.Rows), }, + Termmodes: termModes, } - return + return pty, true } func parseWinchRequest(s []byte) (win Window, ok bool) { From 25548d7584addeabd36b67be9736de0ea7442b88 Mon Sep 17 00:00:00 2001 From: Gzp_ Date: Tue, 17 Dec 2019 01:08:37 +0800 Subject: [PATCH 2/4] Update ssh.go Co-Authored-By: Andrey Petrov --- ssh.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ssh.go b/ssh.go index f3421ee..52555df 100644 --- a/ssh.go +++ b/ssh.go @@ -74,7 +74,7 @@ type Window struct { type Pty struct { Term string Window Window - Termmodes gossh.TerminalModes + TerminalModes gossh.TerminalModes } // Serve accepts incoming SSH connections on the listener l, creating a new From 38f1cb660799959098145a5a17daaabbeb1dcde5 Mon Sep 17 00:00:00 2001 From: elfgzp <741424975@qq.com> Date: Tue, 17 Dec 2019 01:13:09 +0800 Subject: [PATCH 3/4] Update Pty.Termmodes -> Pty.TerminalModes --- session_test.go | 10 +++++----- util.go | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/session_test.go b/session_test.go index 0d509a7..7e8e129 100644 --- a/session_test.go +++ b/session_test.go @@ -199,9 +199,9 @@ func TestPty(t *testing.T) { term := "xterm" winWidth := 40 winHeight := 80 - termModes := make(gossh.TerminalModes) + TerminalModes := make(gossh.TerminalModes) ttyOPOSPEED := uint32(38400) - termModes[gossh.TTY_OP_OSPEED] = ttyOPOSPEED + TerminalModes[gossh.TTY_OP_OSPEED] = ttyOPOSPEED done := make(chan bool) session, _, cleanup := newTestSession(t, &Server{ Handler: func(s Session) { @@ -218,15 +218,15 @@ func TestPty(t *testing.T) { if ptyReq.Window.Height != winHeight { t.Fatalf("expected window height %#v but got %#v", winHeight, ptyReq.Window.Height) } - mode := ptyReq.Termmodes[gossh.TTY_OP_OSPEED] - if ptyReq.Termmodes[gossh.TTY_OP_OSPEED] != ttyOPOSPEED { + mode := ptyReq.TerminalModes[gossh.TTY_OP_OSPEED] + if ptyReq.TerminalModes[gossh.TTY_OP_OSPEED] != ttyOPOSPEED { t.Fatalf("expected mode %#v but got %#v", ttyOPOSPEED, mode) } close(done) }, }, nil) defer cleanup() - if err := session.RequestPty(term, winHeight, winWidth, termModes); err != nil { + if err := session.RequestPty(term, winHeight, winWidth, TerminalModes); err != nil { t.Fatalf("expected nil but got %v", err) } if err := session.Shell(); err != nil { diff --git a/util.go b/util.go index 2c26d5a..77e0b8c 100644 --- a/util.go +++ b/util.go @@ -43,7 +43,7 @@ func parsePtyRequest(s []byte) (pty Pty, ok bool) { Val uint32 }{} - termModes := make(ssh.TerminalModes, 0) + TerminalModes := make(ssh.TerminalModes, 0) for { if len(modes) < 1 || modes[0] == ttyOPEND || len(modes) < 5 { break @@ -53,7 +53,7 @@ func parsePtyRequest(s []byte) (pty Pty, ok bool) { if err != nil { return Pty{}, false } - termModes[mode.Key] = mode.Val + TerminalModes[mode.Key] = mode.Val modes = modes[6:] } @@ -63,7 +63,7 @@ func parsePtyRequest(s []byte) (pty Pty, ok bool) { Width: int(reqMsg.Columns), Height: int(reqMsg.Rows), }, - Termmodes: termModes, + TerminalModes: TerminalModes, } return pty, true } From 59267f6783c72354d3b40b2cb683dcad8b7e95ab Mon Sep 17 00:00:00 2001 From: elfgzp <741424975@qq.com> Date: Tue, 17 Dec 2019 13:10:33 +0800 Subject: [PATCH 4/4] Update ssh.go & util.go --- ssh.go | 4 +-- util.go | 87 ++++++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 69 insertions(+), 22 deletions(-) diff --git a/ssh.go b/ssh.go index 52555df..ffe3e94 100644 --- a/ssh.go +++ b/ssh.go @@ -72,8 +72,8 @@ type Window struct { // Pty represents a PTY request and configuration. type Pty struct { - Term string - Window Window + Term string + Window Window TerminalModes gossh.TerminalModes } diff --git a/util.go b/util.go index 77e0b8c..a41e7cd 100644 --- a/util.go +++ b/util.go @@ -33,39 +33,86 @@ func parsePtyRequest(s []byte) (pty Pty, ok bool) { reqMsg := &ptyRequestMsg{} err := ssh.Unmarshal(s, reqMsg) if err != nil { - return Pty{}, false + return } modes := []byte(reqMsg.Modelist) + terminalModes, ok := parseTerminalModes(modes) + if !ok { + return + } + + pty = Pty{ + Term: reqMsg.Term, + Window: Window{ + Width: int(reqMsg.Columns), + Height: int(reqMsg.Rows), + }, + TerminalModes: terminalModes, + } + return +} + +func makeTerminalModes(terminalModes ssh.TerminalModes) string { + var tm []byte + for k, v := range terminalModes { + kv := struct { + Key byte + Val uint32 + }{k, v} + tm = append(tm, ssh.Marshal(&kv)...) + } + tm = append(tm, ttyOPEND) + return string(tm) +} + +func parseTerminalModes(s []byte) (terminalModes ssh.TerminalModes, ok bool) { mode := struct { Key uint8 Val uint32 }{} - TerminalModes := make(ssh.TerminalModes, 0) + terminalModes = make(ssh.TerminalModes, 0) for { - if len(modes) < 1 || modes[0] == ttyOPEND || len(modes) < 5 { - break - } - b := modes[:5] - err = ssh.Unmarshal(b, &mode) - if err != nil { - return Pty{}, false + if len(s) < 1 { + ok = true + return } - TerminalModes[mode.Key] = mode.Val - modes = modes[6:] - } - pty = Pty{ - Term: reqMsg.Term, - Window: Window{ - Width: int(reqMsg.Columns), - Height: int(reqMsg.Rows), - }, - TerminalModes: TerminalModes, + opcode := s[0] + switch opcode { + case ttyOPEND: + ok = true + return + default: + /* + * SSH2: + * Opcodes 1 to 159 are defined to have a uint32 + * argument. + * Opcodes 160 to 255 are undefined and cause parsing + * to stop. + */ + if opcode > 0 && opcode < 160 { + if len(s) < 5 { + // parse failed + return + } + + b := s[:5] + if err := ssh.Unmarshal(b, &mode); err != nil { + return + } + + terminalModes[mode.Key] = mode.Val + s = s[6:] + + } else { + ok = true + return + } + } } - return pty, true } func parseWinchRequest(s []byte) (win Window, ok bool) {