- commit
- c79df32
- parent
- 92b854a
- author
- Antonio Mika
- date
- 2025-03-12 17:14:03 -0400 EDT
Updates
4 files changed,
+164,
-44
+20,
-6
1@@ -45,24 +45,38 @@ func LogMiddleware(getLogger GetLoggerInterface, db FindUserInterface) SSHServer
2
3 pty, _, ok := s.Pty()
4
5+ width, height := 0, 0
6+ term := ""
7+ if pty != nil {
8+ term = pty.Term
9+ width = pty.Window.Width
10+ height = pty.Window.Height
11+ }
12+
13 logger.Info(
14 "connect",
15 "sshUser", s.User(),
16 "pty", ok,
17- "term", pty.Term,
18- "windowWidth", pty.Window.Width,
19- "windowHeight", pty.Window.Height,
20+ "term", term,
21+ "windowWidth", width,
22+ "windowHeight", height,
23 )
24
25 sshHandler(s)
26
27+ if pty != nil {
28+ term = pty.Term
29+ width = pty.Window.Width
30+ height = pty.Window.Height
31+ }
32+
33 logger.Info(
34 "disconnect",
35 "sshUser", s.User(),
36 "pty", ok,
37- "term", pty.Term,
38- "windowWidth", pty.Window.Width,
39- "windowHeight", pty.Window.Height,
40+ "term", term,
41+ "windowWidth", width,
42+ "windowHeight", height,
43 "duration", time.Since(ct),
44 )
45
+87,
-0
1@@ -1,6 +1,7 @@
2 package pssh
3
4 import (
5+ "encoding/binary"
6 "fmt"
7 )
8
9@@ -33,3 +34,89 @@ func PtyMdw(mdw SSHServerMiddleware) SSHServerMiddleware {
10 }
11 }
12 }
13+
14+type Window struct {
15+ Width int
16+ Height int
17+ HeightPixels int
18+ WidthPixels int
19+}
20+
21+type Pty struct {
22+ Term string
23+ Window Window
24+}
25+
26+func (p *Pty) Resize(width, height int) error {
27+ return nil
28+}
29+
30+func (p *Pty) Name() string {
31+ return ""
32+}
33+
34+func parsePtyRequest(s []byte) (pty Pty, ok bool) {
35+ term, s, ok := parseString(s)
36+ if !ok {
37+ return
38+ }
39+ width32, s, ok := parseUint32(s)
40+ if !ok {
41+ return
42+ }
43+ height32, _, ok := parseUint32(s)
44+ if !ok {
45+ return
46+ }
47+ pty = Pty{
48+ Term: term,
49+ Window: Window{
50+ Width: int(width32),
51+ Height: int(height32),
52+ },
53+ }
54+ return
55+}
56+
57+func parseWinchRequest(s []byte) (win Window, ok bool) {
58+ width32, s, ok := parseUint32(s)
59+ if width32 < 1 {
60+ ok = false
61+ }
62+ if !ok {
63+ return
64+ }
65+ height32, _, ok := parseUint32(s)
66+ if height32 < 1 {
67+ ok = false
68+ }
69+ if !ok {
70+ return
71+ }
72+ win = Window{
73+ Width: int(width32),
74+ Height: int(height32),
75+ }
76+ return
77+}
78+
79+func parseString(in []byte) (out string, rest []byte, ok bool) {
80+ if len(in) < 4 {
81+ return
82+ }
83+ length := binary.BigEndian.Uint32(in)
84+ if uint32(len(in)) < 4+length {
85+ return
86+ }
87+ out = string(in[4 : 4+length])
88+ rest = in[4+length:]
89+ ok = true
90+ return
91+}
92+
93+func parseUint32(in []byte) (uint32, []byte, bool) {
94+ if len(in) < 4 {
95+ return 0, nil, false
96+ }
97+ return binary.BigEndian.Uint32(in), in[4:], true
98+}
+55,
-36
1@@ -7,7 +7,6 @@ import (
2 "fmt"
3 "log/slog"
4 "net"
5- "os"
6 "strings"
7 "sync"
8 "time"
9@@ -34,6 +33,11 @@ func (sc *SSHServerConn) Close() error {
10 type SSHServerConnSession struct {
11 ssh.Channel
12 *SSHServerConn
13+
14+ pty *Pty
15+ winch chan Window
16+
17+ mu sync.Mutex
18 }
19
20 // Deadline implements context.Context.
21@@ -130,29 +134,15 @@ func (s *SSHServerConnSession) Fatal(err error) {
22 _ = s.Close()
23 }
24
25-type Window struct {
26- Width int
27- Height int
28- HeightPixels int
29- WidthPixels int
30-}
31-
32-type Pty struct {
33- Term string
34- Window Window
35- Slave os.File
36-}
37-
38-func (s *SSHServerConnSession) Pty() (Pty, <-chan Window, bool) {
39- return Pty{}, nil, false
40-}
41+func (s *SSHServerConnSession) Pty() (*Pty, <-chan Window, bool) {
42+ s.mu.Lock()
43+ defer s.mu.Unlock()
44
45-func (p Pty) Resize(width, height int) error {
46- return nil
47-}
48+ if s.pty == nil {
49+ return nil, nil, false
50+ }
51
52-func (p Pty) Name() string {
53- return ""
54+ return s.pty, s.winch, true
55 }
56
57 var _ context.Context = &SSHServerConnSession{}
58@@ -335,6 +325,11 @@ func NewSSHServer(ctx context.Context, logger *slog.Logger, config *SSHServerCon
59 return err
60 }
61
62+ sesh := &SSHServerConnSession{
63+ Channel: channel,
64+ SSHServerConn: sc,
65+ }
66+
67 for {
68 select {
69 case <-sc.Done():
70@@ -346,7 +341,8 @@ func NewSSHServer(ctx context.Context, logger *slog.Logger, config *SSHServerCon
71
72 go func() {
73 sc.Logger.Info("new session request", "type", req.Type, "wantReply", req.WantReply, "payload", req.Payload)
74- if req.Type == "subsystem" {
75+ switch req.Type {
76+ case "subsystem":
77 if len(sc.SSHServer.Config.SubsystemMiddleware) == 0 {
78 req.Reply(false, nil)
79 sc.Close()
80@@ -358,11 +354,6 @@ func NewSSHServer(ctx context.Context, logger *slog.Logger, config *SSHServerCon
81 h = m(h)
82 }
83
84- sesh := &SSHServerConnSession{
85- Channel: channel,
86- SSHServerConn: sc,
87- }
88-
89 req.Reply(true, nil)
90
91 if err := h(sesh); err != nil {
92@@ -373,18 +364,15 @@ func NewSSHServer(ctx context.Context, logger *slog.Logger, config *SSHServerCon
93
94 sesh.Exit(0)
95 sesh.Close()
96- return
97- } else if req.Type == "exec" {
98+ case "shell", "exec":
99 if len(sc.SSHServer.Config.Middleware) == 0 {
100 req.Reply(false, nil)
101 }
102
103- sesh := &SSHServerConnSession{
104- Channel: channel,
105- SSHServerConn: sc,
106- }
107+ var payload = struct{ Value string }{}
108+ ssh.Unmarshal(req.Payload, &payload)
109
110- sesh.SetValue("command", strings.Fields(string(req.Payload[4:])))
111+ sesh.SetValue("command", strings.Fields(payload.Value))
112
113 h := func(*SSHServerConnSession) error { return nil }
114 for _, m := range sc.SSHServer.Config.Middleware {
115@@ -401,7 +389,38 @@ func NewSSHServer(ctx context.Context, logger *slog.Logger, config *SSHServerCon
116
117 sesh.Exit(0)
118 sesh.Close()
119- return
120+ case "pty-req":
121+ if sesh.pty != nil {
122+ req.Reply(false, nil)
123+ return
124+ }
125+
126+ ptyReq, ok := parsePtyRequest(req.Payload)
127+ if !ok {
128+ req.Reply(false, nil)
129+ return
130+ }
131+
132+ sesh.mu.Lock()
133+ sesh.pty = &ptyReq
134+ sesh.winch = make(chan Window, 1)
135+ sesh.mu.Unlock()
136+
137+ sesh.winch <- ptyReq.Window
138+ req.Reply(ok, nil)
139+ case "window-change":
140+ if sesh.pty == nil {
141+ req.Reply(false, nil)
142+ return
143+ }
144+ win, ok := parseWinchRequest(req.Payload)
145+ if ok {
146+ sesh.mu.Lock()
147+ sesh.pty.Window = win
148+ sesh.winch <- win
149+ sesh.mu.Unlock()
150+ }
151+ req.Reply(ok, nil)
152 }
153 }()
154 }
1@@ -17,7 +17,7 @@ type consoleData struct {
2
3 type VConsole struct {
4 Session *pssh.SSHServerConnSession
5- pty pssh.Pty
6+ pty *pssh.Pty
7
8 sizeEnableOnce sync.Once
9
10@@ -108,7 +108,7 @@ func (v *VConsole) Size() (console.WinSize, error) {
11 }
12
13 func (v *VConsole) Fd() uintptr {
14- return v.pty.Slave.Fd()
15+ return 0
16 }
17
18 func (v *VConsole) Name() string {