@@ -16,61 +16,134 @@ func generateSigner() (ssh.Signer, error) {
16
16
return ssh .NewSignerFromKey (key )
17
17
}
18
18
19
- func parsePtyRequest (s []byte ) (pty Pty , ok bool ) {
20
- term , s , ok := parseString (s )
19
+ func parsePtyRequest (payload []byte ) (pty Pty , ok bool ) {
20
+ // From https://datatracker.ietf.org/doc/html/rfc4254
21
+ // 6.2. Requesting a Pseudo-Terminal
22
+ // A pseudo-terminal can be allocated for the session by sending the
23
+ // following message.
24
+ // byte SSH_MSG_CHANNEL_REQUEST
25
+ // uint32 recipient channel
26
+ // string "pty-req"
27
+ // boolean want_reply
28
+ // string TERM environment variable value (e.g., vt100)
29
+ // uint32 terminal width, characters (e.g., 80)
30
+ // uint32 terminal height, rows (e.g., 24)
31
+ // uint32 terminal width, pixels (e.g., 640)
32
+ // uint32 terminal height, pixels (e.g., 480)
33
+ // string encoded terminal modes
34
+
35
+ // The payload starts from the TERM variable.
36
+ term , rem , ok := parseString (payload )
21
37
if ! ok {
22
38
return
23
39
}
24
- width32 , s , ok := parseUint32 ( s )
40
+ win , rem , ok := parseWindow ( rem )
25
41
if ! ok {
26
42
return
27
43
}
28
- height32 , _ , ok := parseUint32 ( s )
44
+ modes , ok := parseTerminalModes ( rem )
29
45
if ! ok {
30
46
return
31
47
}
32
48
pty = Pty {
33
- Term : term ,
34
- Window : Window {
35
- Width : int (width32 ),
36
- Height : int (height32 ),
37
- },
49
+ Term : term ,
50
+ Window : win ,
51
+ Modes : modes ,
38
52
}
39
53
return
40
54
}
41
55
42
- func parseWinchRequest (s []byte ) (win Window , ok bool ) {
43
- width32 , s , ok := parseUint32 (s )
44
- if width32 < 1 {
45
- ok = false
56
+ func parseTerminalModes (in []byte ) (modes ssh.TerminalModes , ok bool ) {
57
+ // From https://datatracker.ietf.org/doc/html/rfc4254
58
+ // 8. Encoding of Terminal Modes
59
+ //
60
+ // All 'encoded terminal modes' (as passed in a pty request) are encoded
61
+ // into a byte stream. It is intended that the coding be portable
62
+ // across different environments. The stream consists of opcode-
63
+ // argument pairs wherein the opcode is a byte value. Opcodes 1 to 159
64
+ // have a single uint32 argument. Opcodes 160 to 255 are not yet
65
+ // defined, and cause parsing to stop (they should only be used after
66
+ // any other data). The stream is terminated by opcode TTY_OP_END
67
+ // (0x00).
68
+ //
69
+ // The client SHOULD put any modes it knows about in the stream, and the
70
+ // server MAY ignore any modes it does not know about. This allows some
71
+ // degree of machine-independence, at least between systems that use a
72
+ // POSIX-like tty interface. The protocol can support other systems as
73
+ // well, but the client may need to fill reasonable values for a number
74
+ // of parameters so the server pty gets set to a reasonable mode (the
75
+ // server leaves all unspecified mode bits in their default values, and
76
+ // only some combinations make sense).
77
+ _ , rem , ok := parseUint32 (in )
78
+ if ! ok {
79
+ return
80
+ }
81
+ const ttyOpEnd = 0
82
+ for len (rem ) > 0 {
83
+ if modes == nil {
84
+ modes = make (ssh.TerminalModes )
85
+ }
86
+ code := uint8 (rem [0 ])
87
+ rem = rem [1 :]
88
+ if code == ttyOpEnd || code > 160 {
89
+ break
90
+ }
91
+ var val uint32
92
+ val , rem , ok = parseUint32 (rem )
93
+ if ! ok {
94
+ return
95
+ }
96
+ modes [code ] = val
97
+ }
98
+ ok = true
99
+ return
100
+ }
101
+
102
+ func parseWindow (s []byte ) (win Window , rem []byte , ok bool ) {
103
+ // 6.7. Window Dimension Change Message
104
+ // When the window (terminal) size changes on the client side, it MAY
105
+ // send a message to the other side to inform it of the new dimensions.
106
+
107
+ // byte SSH_MSG_CHANNEL_REQUEST
108
+ // uint32 recipient channel
109
+ // string "window-change"
110
+ // boolean FALSE
111
+ // uint32 terminal width, columns
112
+ // uint32 terminal height, rows
113
+ // uint32 terminal width, pixels
114
+ // uint32 terminal height, pixels
115
+ wCols , rem , ok := parseUint32 (s )
116
+ if ! ok {
117
+ return
46
118
}
119
+ hRows , rem , ok := parseUint32 (rem )
47
120
if ! ok {
48
121
return
49
122
}
50
- height32 , _ , ok := parseUint32 (s )
51
- if height32 < 1 {
52
- ok = false
123
+ wPixels , rem , ok := parseUint32 (rem )
124
+ if ! ok {
125
+ return
53
126
}
127
+ hPixels , rem , ok := parseUint32 (rem )
54
128
if ! ok {
55
129
return
56
130
}
57
131
win = Window {
58
- Width : int (width32 ),
59
- Height : int (height32 ),
132
+ Width : int (wCols ),
133
+ Height : int (hRows ),
134
+ WidthPixels : int (wPixels ),
135
+ HeightPixels : int (hPixels ),
60
136
}
61
137
return
62
138
}
63
139
64
- func parseString (in []byte ) (out string , rest []byte , ok bool ) {
65
- if len (in ) < 4 {
66
- return
67
- }
68
- length := binary .BigEndian .Uint32 (in )
69
- if uint32 (len (in )) < 4 + length {
140
+ func parseString (in []byte ) (out string , rem []byte , ok bool ) {
141
+ length , rem , ok := parseUint32 (in )
142
+ if uint32 (len (rem )) < length || ! ok {
143
+ ok = false
70
144
return
71
145
}
72
- out = string (in [4 : 4 + length ])
73
- rest = in [4 + length :]
146
+ out , rem = string (rem [:length ]), rem [length :]
74
147
ok = true
75
148
return
76
149
}
0 commit comments