ssh-chat/server.go

125 lines
2.6 KiB
Go
Raw Normal View History

2014-12-07 08:31:23 +01:00
package main
import (
"fmt"
"net"
2014-12-10 00:51:24 +01:00
"strings"
"sync"
"golang.org/x/crypto/ssh"
2014-12-07 08:31:23 +01:00
)
type Server struct {
sshConfig *ssh.ServerConfig
sshSigner *ssh.Signer
done chan struct{}
2014-12-10 03:22:46 +01:00
clients map[Client]struct{}
2014-12-10 00:51:24 +01:00
lock sync.Mutex
2014-12-07 08:31:23 +01:00
}
func NewServer(privateKey []byte) (*Server, error) {
signer, err := ssh.ParsePrivateKey(privateKey)
if err != nil {
return nil, err
}
config := ssh.ServerConfig{
2014-12-10 00:51:24 +01:00
NoClientAuth: false,
PasswordCallback: func(conn ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) {
return nil, nil
},
PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
return nil, nil
},
2014-12-07 08:31:23 +01:00
}
config.AddHostKey(signer)
server := Server{
sshConfig: &config,
sshSigner: &signer,
2014-12-10 00:51:24 +01:00
done: make(chan struct{}),
2014-12-10 03:22:46 +01:00
clients: map[Client]struct{}{},
2014-12-07 08:31:23 +01:00
}
return &server, nil
}
2014-12-10 02:01:35 +01:00
func (s *Server) Broadcast(msg string, except *Client) {
2014-12-10 00:51:24 +01:00
logger.Debugf("Broadcast to %d: %s", len(s.clients), strings.TrimRight(msg, "\r\n"))
2014-12-10 03:22:46 +01:00
for client := range s.clients {
2014-12-10 02:01:35 +01:00
if except != nil && client == *except {
continue
}
2014-12-10 00:51:24 +01:00
client.Msg <- msg
2014-12-07 08:31:23 +01:00
}
}
2014-12-10 00:51:24 +01:00
func (s *Server) Start(laddr string) error {
2014-12-07 08:31:23 +01:00
// Once a ServerConfig has been configured, connections can be
// accepted.
socket, err := net.Listen("tcp", laddr)
if err != nil {
2014-12-10 00:51:24 +01:00
return err
2014-12-07 08:31:23 +01:00
}
logger.Infof("Listening on %s", laddr)
go func() {
for {
conn, err := socket.Accept()
2014-12-10 00:51:24 +01:00
2014-12-07 08:31:23 +01:00
if err != nil {
2014-12-10 00:51:24 +01:00
// TODO: Handle shutdown more gracefully?
2014-12-07 08:31:23 +01:00
logger.Errorf("Failed to accept connection, aborting loop: %v", err)
return
}
2014-12-10 00:51:24 +01:00
// Goroutineify to resume accepting sockets early.
go func() {
// From a standard TCP connection to an encrypted SSH connection
sshConn, channels, requests, err := ssh.NewServerConn(conn, s.sshConfig)
if err != nil {
logger.Errorf("Failed to handshake: %v", err)
return
}
logger.Infof("Connection from: %s, %s, %s", sshConn.RemoteAddr(), sshConn.User(), sshConn.ClientVersion())
go ssh.DiscardRequests(requests)
client := NewClient(s, sshConn.User())
// TODO: mutex this
2014-12-07 08:31:23 +01:00
2014-12-10 00:51:24 +01:00
s.lock.Lock()
2014-12-10 03:22:46 +01:00
s.clients[*client] = struct{}{}
2014-12-10 02:01:35 +01:00
num := len(s.clients)
2014-12-10 00:51:24 +01:00
s.lock.Unlock()
2014-12-07 08:31:23 +01:00
2014-12-10 02:01:35 +01:00
s.Broadcast(fmt.Sprintf("* Joined: %s (%d present)\r\n", client.Name, num), nil)
2014-12-10 00:51:24 +01:00
2014-12-10 03:22:46 +01:00
go func() {
sshConn.Wait()
s.lock.Lock()
delete(s.clients, *client)
s.lock.Unlock()
s.Broadcast(fmt.Sprintf("* Left: %s\r\n", client.Name), nil)
}()
2014-12-10 00:51:24 +01:00
go client.handleChannels(channels)
}()
2014-12-07 08:31:23 +01:00
}
}()
2014-12-10 00:51:24 +01:00
go func() {
<-s.done
socket.Close()
}()
return nil
2014-12-07 08:31:23 +01:00
}
2014-12-10 00:51:24 +01:00
func (s *Server) Stop() {
close(s.done)
2014-12-07 08:31:23 +01:00
}