2015-05-30 15:19:10 +00:00
package easyssh
import (
"fmt"
2016-07-30 12:35:10 +00:00
"io"
"io/ioutil"
2015-05-30 15:19:10 +00:00
"log"
"sync"
"golang.org/x/crypto/ssh"
)
2016-07-30 12:35:10 +00:00
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 )
}
2015-05-30 15:19:10 +00:00
// 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
2016-07-30 12:35:10 +00:00
func ( s * GlobalMultipleRequestsMux ) HandleRequestFunc ( requestType string , f func ( * ssh . Request , ssh . Conn ) ) {
2015-05-30 15:19:10 +00:00
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
2016-07-30 12:35:10 +00:00
func ( s * ChannelsMux ) HandleChannelFunc ( channelType string , f func ( newChannel ssh . NewChannel , channel ssh . Channel , reqs <- chan * ssh . Request , sshConn ssh . Conn ) ) {
2015-05-30 15:19:10 +00:00
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 )
}
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 {
2016-07-30 12:35:10 +00:00
logger . Printf ( "Received channel: %v" , newChannel . ChannelType ( ) )
2015-05-30 15:19:10 +00:00
// Check the type of channel
t := newChannel . ChannelType ( )
s . channelMutex . RLock ( )
handler , ok := s . channels [ t ]
s . channelMutex . RUnlock ( )
if ! ok {
2016-07-30 12:35:10 +00:00
logger . Printf ( "Unknown channel type: %s" , t )
2015-05-30 15:19:10 +00:00
newChannel . Reject ( ssh . UnknownChannelType , fmt . Sprintf ( "unknown channel type: %s" , t ) )
continue
}
channel , requests , err := newChannel . Accept ( )
if err != nil {
2016-07-30 12:35:10 +00:00
logger . Printf ( "could not accept channel (%s)" , err )
2015-05-30 15:19:10 +00:00
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
2016-07-30 12:35:10 +00:00
func HandleRequestFunc ( requestType string , handler func ( * ssh . Request , ssh . Conn ) ) {
DefaultGlobalMultipleRequestsHandler . HandleRequestFunc ( requestType , GlobalRequestHandlerFunc ( handler ) )
2015-05-30 15:19:10 +00:00
}
// 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
2016-07-30 12:35:10 +00:00
func HandleChannelFunc ( channelType string , handler func ( newChannel ssh . NewChannel , channel ssh . Channel , reqs <- chan * ssh . Request , sshConn ssh . Conn ) ) {
DefaultMultipleChannelsHandler . HandleChannelFunc ( channelType , ChannelHandlerFunc ( handler ) )
2015-05-30 15:19:10 +00:00
}
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 )
}