forked from ejohn/easyssh
-
Notifications
You must be signed in to change notification settings - Fork 0
/
common.go
306 lines (241 loc) · 12.1 KB
/
common.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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
package easyssh
import (
"fmt"
"io"
"io/ioutil"
"log"
"sync"
"golang.org/x/crypto/ssh"
)
var logger *log.Logger
func init() {
logger = log.New(ioutil.Discard, "easyssh", 0)
}
// EnableLogging enables logging for the easyssh library
func EnableLogging(output io.Writer) {
logger.SetOutput(output)
}
// A ConnHandler is a top level SSH Manager. Objects implementing the ConnHandler are responsible for managing incoming Channels and Global Requests
type ConnHandler interface {
HandleSSHConn(ssh.Conn, <-chan ssh.NewChannel, <-chan *ssh.Request)
}
// ConnHandlerFunc is an adapter that allows regular functions to act as SSH Connection Handlers
type ConnHandlerFunc func(ssh.Conn, <-chan ssh.NewChannel, <-chan *ssh.Request)
// HandleSSHConn calls f(sshConn, chans, reqs)
func (f ConnHandlerFunc) HandleSSHConn(sshConn ssh.Conn, chans <-chan ssh.NewChannel, reqs <-chan *ssh.Request) {
f(sshConn, chans, reqs)
}
// ChannelHandler handles channel requests for a given channel type
type ChannelHandler interface {
HandleChannel(newChannel ssh.NewChannel, channel ssh.Channel, reqs <-chan *ssh.Request, sshConn ssh.Conn)
}
// ChannelHandlerFunc is an adapter that allows regular functions to act as SSH Channel Handlers
type ChannelHandlerFunc func(newChannel ssh.NewChannel, channel ssh.Channel, reqs <-chan *ssh.Request, sshConn ssh.Conn)
// HandleChannel calls f(channelType, channel, reqs, sshConn)
func (f ChannelHandlerFunc) HandleChannel(newChannel ssh.NewChannel, channel ssh.Channel, reqs <-chan *ssh.Request, sshConn ssh.Conn) {
f(newChannel, channel, reqs, sshConn)
}
// MultipleChannelsHandler handles a chan of all SSH channel requests for a connection
type MultipleChannelsHandler interface {
HandleChannels(chans <-chan ssh.NewChannel, sshConn ssh.Conn)
}
// MultipleChannelsHandlerFunc is an adapter that allows regular functions to act as SSH Multiple Channels Handlers
type MultipleChannelsHandlerFunc func(chans <-chan ssh.NewChannel, sshConn ssh.Conn)
// HandleChannels calls f(chans, sshConn)
func (f MultipleChannelsHandlerFunc) HandleChannels(chans <-chan ssh.NewChannel, sshConn ssh.Conn) {
f(chans, sshConn)
}
// GlobalMultipleRequestsHandler handles global (not tied to a channel) out-of-band SSH Requests
type GlobalMultipleRequestsHandler interface {
HandleRequests(<-chan *ssh.Request, ssh.Conn)
}
// GlobalMultipleRequestsHandlerFunc is an adaper to allow regular functions to act as a Global Requests Handler
type GlobalMultipleRequestsHandlerFunc func(<-chan *ssh.Request, ssh.Conn)
// HandleRequests calls f(reqs, sshConn)
func (f GlobalMultipleRequestsHandlerFunc) HandleRequests(reqs <-chan *ssh.Request, sshConn ssh.Conn) {
f(reqs, sshConn)
}
// DiscardGlobalMultipleRequests is a wrapper around ssh.DiscardRequests. Ignores ssh ServerConn
func DiscardGlobalMultipleRequests(reqs <-chan *ssh.Request, sshConn ssh.Conn) {
ssh.DiscardRequests(reqs)
}
// DiscardRequest appropriately discards SSH Requests, returning responses to those that expect it
func DiscardRequest(req *ssh.Request) {
if req.WantReply {
req.Reply(false, nil)
}
}
// GlobalRequestHandler handles global (not tied to a channel) out-of-band SSH Requests
type GlobalRequestHandler interface {
HandleRequest(*ssh.Request, ssh.Conn)
}
// GlobalRequestHandlerFunc is an adaper to allow regular functions to act as a Global Request Handler
type GlobalRequestHandlerFunc func(*ssh.Request, ssh.Conn)
// HandleRequest calls f(reqs, sshConn)
func (f GlobalRequestHandlerFunc) HandleRequest(req *ssh.Request, sshConn ssh.Conn) {
f(req, sshConn)
}
// ChannelMultipleRequestsHandler handles tied to a channel out-of-band SSH Requests
type ChannelMultipleRequestsHandler interface {
HandleMultipleRequests(reqs <-chan *ssh.Request, sshConn ssh.Conn, channelType string, channel ssh.Channel)
}
// ChannelMultipleRequestsHandlerFunc is an adaper to allow regular functions to act as a Channel Requests Handler
type ChannelMultipleRequestsHandlerFunc func(reqs <-chan *ssh.Request, sshConn ssh.Conn, channelType string, channel ssh.Channel)
// HandleMultipleRequests calls f(reqs, sshConn, channelType, channel)
func (f ChannelMultipleRequestsHandlerFunc) HandleMultipleRequests(reqs <-chan *ssh.Request, sshConn ssh.Conn, channelType string, channel ssh.Channel) {
f(reqs, sshConn, channelType, channel)
}
// DiscardChannelMultipleRequests is a wrapper around ssh.DiscardRequests. Ignores ssh ServerConn
func DiscardChannelMultipleRequests(reqs <-chan *ssh.Request, sshConn ssh.Conn, channelType string, channel ssh.Channel) {
ssh.DiscardRequests(reqs)
}
// ChannelRequestHandler handles tied to a channel out-of-band SSH Requests
type ChannelRequestHandler interface {
HandleRequest(req *ssh.Request, sshConn ssh.Conn, channelType string, channel ssh.Channel)
}
// ChannelRequestHandlerFunc is an adaper to allow regular functions to act as a Global Request Handler
type ChannelRequestHandlerFunc func(req *ssh.Request, sshConn ssh.Conn, channelType string, channel ssh.Channel)
// HandleRequest calls f(reqs, sshConn, channelType, channel)
func (f ChannelRequestHandlerFunc) HandleRequest(req *ssh.Request, sshConn ssh.Conn, channelType string, channel ssh.Channel) {
f(req, sshConn, channelType, channel)
}
// SSHConnHandler is an SSH Channel multiplexer. It matches Channel types and calls the handler for the corresponding type
type SSHConnHandler struct {
MultipleChannelsHandler
GlobalMultipleRequestsHandler
}
// GlobalMultipleRequestsMux is an SSH Global Requests multiplexer. It matches Channel types and calls the handler for the corresponding type - can be used as GlobalMultipleRequestsHandler
type GlobalMultipleRequestsMux struct {
requestMutex sync.RWMutex
requests map[string]GlobalRequestHandler
}
// NewGlobalMultipleRequestsMux creates and returns a GlobalMultipleRequestsHandler that performs multiplexing of request types with dispatching to GlobalRequestHandlers
func NewGlobalMultipleRequestsMux() *GlobalMultipleRequestsMux {
return &GlobalMultipleRequestsMux{requests: map[string]GlobalRequestHandler{}}
}
// ChannelsMux is an SSH Channel multiplexer. It matches Channel types and calls the handler for the corresponding type - Can be used as ChannelsHandler
type ChannelsMux struct {
channelMutex sync.RWMutex
channels map[string]ChannelHandler
}
// NewChannelsMux creates and returns a MultipleChannelsHandler that performs multiplexing of request types with dispatching to ChannelHandlers
func NewChannelsMux() *ChannelsMux {
return &ChannelsMux{channels: map[string]ChannelHandler{}}
}
// NewSSHConnHandler creates and returns a basic working ConnHandler to provide a minimal "working" SSH server
func NewSSHConnHandler() *SSHConnHandler {
return &SSHConnHandler{MultipleChannelsHandler: NewChannelsMux(), GlobalMultipleRequestsHandler: NewGlobalMultipleRequestsMux()}
}
// HandleSSHConn manages incoming channel and out-of-band requests. It discards out-of-band requests and dispatches channel requests if a ChannelHandler is registered for a given Channel Type
func (s *SSHConnHandler) HandleSSHConn(sshConn ssh.Conn, chans <-chan ssh.NewChannel, reqs <-chan *ssh.Request) {
go globalRequestsHandler{s}.HandleRequests(reqs, sshConn)
go channelsHandler{s}.HandleChannels(chans, sshConn)
}
// HandleRequest registers the GlobalRequestHandler for the given Channel Type. If a GlobalRequestHandler was already registered for the type, HandleRequest panics
func (s *GlobalMultipleRequestsMux) HandleRequest(requestType string, handler GlobalRequestHandler) {
s.requestMutex.Lock()
defer s.requestMutex.Unlock()
_, ok := s.requests[requestType]
if ok {
panic("easyssh: GlobalRequestHandler already registered for " + requestType)
}
s.requests[requestType] = handler
}
// HandleRequestFunc registers the Channel Handler function for the provided Request Type
func (s *GlobalMultipleRequestsMux) HandleRequestFunc(requestType string, f func(*ssh.Request, ssh.Conn)) {
s.HandleRequest(requestType, GlobalRequestHandlerFunc(f))
}
// HandleChannel registers the ChannelHandler for the given Channel Type. If a ChannelHandler was already registered for the type, HandleChannel panics
func (s *ChannelsMux) HandleChannel(channelType string, handler ChannelHandler) {
s.channelMutex.Lock()
defer s.channelMutex.Unlock()
_, ok := s.channels[channelType]
if ok {
panic("easyssh: ChannelHandler already registered for " + channelType)
}
s.channels[channelType] = handler
}
// HandleChannelFunc registers the Channel Handler function for the provided Channel Type
func (s *ChannelsMux) HandleChannelFunc(channelType string, f func(newChannel ssh.NewChannel, channel ssh.Channel, reqs <-chan *ssh.Request, sshConn ssh.Conn)) {
s.HandleChannel(channelType, ChannelHandlerFunc(f))
}
// HandleRequests handles global out-of-band SSH Requests -
func (s *GlobalMultipleRequestsMux) HandleRequests(reqs <-chan *ssh.Request, sshConn ssh.Conn) {
for req := range reqs {
t := req.Type
s.requestMutex.RLock()
handler, ok := s.requests[t]
if !ok {
DiscardRequest(req)
continue
}
s.requestMutex.RUnlock()
go handler.HandleRequest(req, sshConn)
}
}
// HandleChannels acts a a mux for incoming channel requests
func (s *ChannelsMux) HandleChannels(chans <-chan ssh.NewChannel, sshConn ssh.Conn) {
for newChannel := range chans {
logger.Printf("Received channel: %v", newChannel.ChannelType())
// Check the type of channel
t := newChannel.ChannelType()
s.channelMutex.RLock()
handler, ok := s.channels[t]
s.channelMutex.RUnlock()
if !ok {
logger.Printf("Unknown channel type: %s", t)
newChannel.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %s", t))
continue
}
channel, requests, err := newChannel.Accept()
if err != nil {
logger.Printf("could not accept channel (%s)", err)
continue
}
go handler.HandleChannel(newChannel, channel, requests, sshConn)
}
}
// DefaultSSHConnHandler is an SSH Server Handler
var DefaultSSHConnHandler = &SSHConnHandler{}
// DefaultMultipleChannelsHandler is the ChannelMux and is used to handle all incoming channel requests
var DefaultMultipleChannelsHandler = NewChannelsMux()
// DefaultGlobalMultipleRequestsHandler is a GlobalMultipleRequestsHandler that by default discards all incoming global requests
var DefaultGlobalMultipleRequestsHandler = NewGlobalMultipleRequestsMux()
// HandleRequest registers the given handler with the DefaultGlobalMultipleRequestsHandler
func HandleRequest(requestType string, handler GlobalRequestHandler) {
DefaultGlobalMultipleRequestsHandler.HandleRequest(requestType, handler)
}
// HandleRequestFunc registers the given handler function with the DefaultGlobalMultipleRequestsHandler
func HandleRequestFunc(requestType string, handler func(*ssh.Request, ssh.Conn)) {
DefaultGlobalMultipleRequestsHandler.HandleRequestFunc(requestType, GlobalRequestHandlerFunc(handler))
}
// HandleChannel registers the given handler under the channelType with the DefaultMultipleChannelsHandler
func HandleChannel(channelType string, handler ChannelHandler) {
DefaultMultipleChannelsHandler.HandleChannel(channelType, handler)
}
// HandleChannelFunc registers the given handler function under the channelType with the DefaultMultipleChannelsHandler
func HandleChannelFunc(channelType string, handler func(newChannel ssh.NewChannel, channel ssh.Channel, reqs <-chan *ssh.Request, sshConn ssh.Conn)) {
DefaultMultipleChannelsHandler.HandleChannelFunc(channelType, ChannelHandlerFunc(handler))
}
type channelsHandler struct {
s *SSHConnHandler
}
// HandleChannels is a wrapper, tests if the SSHConnHandler has a MultipleChannelsHandler, and if not uses the default one
func (s channelsHandler) HandleChannels(chans <-chan ssh.NewChannel, sshConn ssh.Conn) {
handler := s.s.MultipleChannelsHandler
if handler == nil {
handler = DefaultMultipleChannelsHandler
}
handler.HandleChannels(chans, sshConn)
}
type globalRequestsHandler struct {
s *SSHConnHandler
}
// HandleRequests is a wrapper, tests if the SSHConnHandler has a GlobalMultipleRequestsHandler, and if not uses the default one
func (s globalRequestsHandler) HandleRequests(reqs <-chan *ssh.Request, sshConn ssh.Conn) {
handler := s.s.GlobalMultipleRequestsHandler
if handler == nil {
handler = DefaultGlobalMultipleRequestsHandler
}
handler.HandleRequests(reqs, sshConn)
}