@@ -16,61 +16,134 @@ func generateSigner() (ssh.Signer, error) {
1616	return  ssh .NewSignerFromKey (key )
1717}
1818
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 )
2137	if  ! ok  {
2238		return 
2339	}
24- 	width32 ,  s , ok  :=  parseUint32 ( s )
40+ 	win ,  rem , ok  :=  parseWindow ( rem )
2541	if  ! ok  {
2642		return 
2743	}
28- 	height32 ,  _ ,  ok  :=  parseUint32 ( s )
44+ 	modes ,  ok  :=  parseTerminalModes ( rem )
2945	if  ! ok  {
3046		return 
3147	}
3248	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 ,
3852	}
3953	return 
4054}
4155
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 
46118	}
119+ 	hRows , rem , ok  :=  parseUint32 (rem )
47120	if  ! ok  {
48121		return 
49122	}
50- 	height32 ,  _ , ok  :=  parseUint32 (s )
51- 	if  height32   <   1  {
52- 		ok   =   false 
123+ 	wPixels ,  rem , ok  :=  parseUint32 (rem )
124+ 	if  ! ok  {
125+ 		return 
53126	}
127+ 	hPixels , rem , ok  :=  parseUint32 (rem )
54128	if  ! ok  {
55129		return 
56130	}
57131	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 ),
60136	}
61137	return 
62138}
63139
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 
70144		return 
71145	}
72- 	out  =  string (in [4  : 4 + length ])
73- 	rest  =  in [4 + length :]
146+ 	out , rem  =  string (rem [:length ]), rem [length :]
74147	ok  =  true 
75148	return 
76149}
0 commit comments