easyssh/common.go

306 lines
12 KiB
Go

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)
}
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)
}