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