mirror of
https://github.com/shazow/ssh-chat.git
synced 2025-04-15 00:20:37 +03:00
Better makefile and working chat.
This commit is contained in:
parent
d2aec8cc44
commit
1041ae3dda
22
Makefile
Normal file
22
Makefile
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
BINARY = ssh-chat
|
||||||
|
KEY = host_key
|
||||||
|
PORT = 2022
|
||||||
|
|
||||||
|
all: $(BINARY)
|
||||||
|
|
||||||
|
**/*.go:
|
||||||
|
go build ./...
|
||||||
|
|
||||||
|
$(BINARY): **/*.go *.go
|
||||||
|
go build .
|
||||||
|
|
||||||
|
build: $(BINARY)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm $(BINARY)
|
||||||
|
|
||||||
|
key: $(KEY)
|
||||||
|
ssh-keygen -f $(KEY) -P ''
|
||||||
|
|
||||||
|
run: $(BINARY) $(KEY)
|
||||||
|
./$(BINARY) -i $(KEY) -b ":$(PORT)" -vv
|
92
client.go
Normal file
92
client.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
|
)
|
||||||
|
|
||||||
|
const MSG_BUFFER = 10
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
Server *Server
|
||||||
|
Msg chan string
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(server *Server, name string) *Client {
|
||||||
|
if name == "" {
|
||||||
|
name = "Anonymoose"
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Client{
|
||||||
|
Server: server,
|
||||||
|
Name: name,
|
||||||
|
Msg: make(chan string, MSG_BUFFER),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) handleShell(channel ssh.Channel) {
|
||||||
|
defer channel.Close()
|
||||||
|
|
||||||
|
term := terminal.NewTerminal(channel, "")
|
||||||
|
|
||||||
|
for {
|
||||||
|
line, err := term.ReadLine()
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
switch line {
|
||||||
|
case "/exit":
|
||||||
|
channel.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := fmt.Sprintf("%s: %s\r\n", c.Name, line)
|
||||||
|
c.Server.Broadcast(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) handleChannels(channels <-chan ssh.NewChannel) {
|
||||||
|
for ch := range channels {
|
||||||
|
if t := ch.ChannelType(); t != "session" {
|
||||||
|
ch.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %s", t))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
channel, requests, err := ch.Accept()
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("Could not accept channel: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
go func(in <-chan *ssh.Request) {
|
||||||
|
defer channel.Close()
|
||||||
|
for req := range in {
|
||||||
|
ok := false
|
||||||
|
switch req.Type {
|
||||||
|
case "shell":
|
||||||
|
if len(req.Payload) == 0 {
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
case "pty-req":
|
||||||
|
// Setup PTY
|
||||||
|
ok = true
|
||||||
|
}
|
||||||
|
req.Reply(ok, nil)
|
||||||
|
}
|
||||||
|
}(requests)
|
||||||
|
|
||||||
|
go c.handleShell(channel)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for msg := range c.Msg {
|
||||||
|
channel.Write([]byte(msg))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// We don't care about other channels?
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
11
cmd.go
11
cmd.go
@ -59,17 +59,14 @@ func main() {
|
|||||||
// Construct interrupt handler
|
// Construct interrupt handler
|
||||||
sig := make(chan os.Signal, 1)
|
sig := make(chan os.Signal, 1)
|
||||||
signal.Notify(sig, os.Interrupt)
|
signal.Notify(sig, os.Interrupt)
|
||||||
go func() {
|
|
||||||
<-sig // Wait for ^C signal
|
|
||||||
logger.Warningf("Interrupt signal detected, shutting down.")
|
|
||||||
server.Stop()
|
|
||||||
}()
|
|
||||||
|
|
||||||
done, err := server.Start(options.Bind)
|
err = server.Start(options.Bind)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Errorf("Failed to start server: %v", err)
|
logger.Errorf("Failed to start server: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
<-done
|
<-sig // Wait for ^C signal
|
||||||
|
logger.Warningf("Interrupt signal detected, shutting down.")
|
||||||
|
server.Stop()
|
||||||
}
|
}
|
||||||
|
136
server.go
136
server.go
@ -1,19 +1,20 @@
|
|||||||
// TODO: NoClientAuth
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
sshConfig *ssh.ServerConfig
|
sshConfig *ssh.ServerConfig
|
||||||
sshSigner *ssh.Signer
|
sshSigner *ssh.Signer
|
||||||
socket *net.Listener
|
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
|
clients []Client
|
||||||
|
lock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(privateKey []byte) (*Server, error) {
|
func NewServer(privateKey []byte) (*Server, error) {
|
||||||
@ -23,120 +24,87 @@ func NewServer(privateKey []byte) (*Server, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
config := ssh.ServerConfig{
|
config := ssh.ServerConfig{
|
||||||
NoClientAuth: true,
|
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
|
||||||
|
},
|
||||||
}
|
}
|
||||||
config.AddHostKey(signer)
|
config.AddHostKey(signer)
|
||||||
|
|
||||||
server := Server{
|
server := Server{
|
||||||
sshConfig: &config,
|
sshConfig: &config,
|
||||||
sshSigner: &signer,
|
sshSigner: &signer,
|
||||||
|
done: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
return &server, nil
|
return &server, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleShell(channel ssh.Channel) {
|
func (s *Server) Broadcast(msg string) {
|
||||||
defer channel.Close()
|
logger.Debugf("Broadcast to %d: %s", len(s.clients), strings.TrimRight(msg, "\r\n"))
|
||||||
|
for _, client := range s.clients {
|
||||||
term := terminal.NewTerminal(channel, "")
|
client.Msg <- msg
|
||||||
|
|
||||||
for {
|
|
||||||
line, err := term.ReadLine()
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
switch line {
|
|
||||||
case "exit":
|
|
||||||
channel.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
term.Write([]byte("you wrote: " + string(line) + "\r\n"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleChannels(channels <-chan ssh.NewChannel) {
|
func (s *Server) Start(laddr string) error {
|
||||||
for ch := range channels {
|
|
||||||
if t := ch.ChannelType(); t != "session" {
|
|
||||||
ch.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %s", t))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
channel, requests, err := ch.Accept()
|
|
||||||
if err != nil {
|
|
||||||
logger.Errorf("Could not accept channel: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
go func(in <-chan *ssh.Request) {
|
|
||||||
defer channel.Close()
|
|
||||||
for req := range in {
|
|
||||||
logger.Infof("Request: ", req.Type, string(req.Payload))
|
|
||||||
|
|
||||||
ok := false
|
|
||||||
switch req.Type {
|
|
||||||
case "shell":
|
|
||||||
// We don't accept any commands (Payload),
|
|
||||||
// only the default shell.
|
|
||||||
if len(req.Payload) == 0 {
|
|
||||||
ok = true
|
|
||||||
}
|
|
||||||
case "pty-req":
|
|
||||||
// Responding 'ok' here will let the client
|
|
||||||
// know we have a pty ready for input
|
|
||||||
ok = true
|
|
||||||
case "window-change":
|
|
||||||
continue //no response
|
|
||||||
}
|
|
||||||
req.Reply(ok, nil)
|
|
||||||
}
|
|
||||||
}(requests)
|
|
||||||
|
|
||||||
go s.handleShell(channel)
|
|
||||||
|
|
||||||
channel.Write([]byte("Hello"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Server) Start(laddr string) (<-chan struct{}, error) {
|
|
||||||
// Once a ServerConfig has been configured, connections can be
|
// Once a ServerConfig has been configured, connections can be
|
||||||
// accepted.
|
// accepted.
|
||||||
socket, err := net.Listen("tcp", laddr)
|
socket, err := net.Listen("tcp", laddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.socket = &socket
|
|
||||||
logger.Infof("Listening on %s", laddr)
|
logger.Infof("Listening on %s", laddr)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
conn, err := socket.Accept()
|
conn, err := socket.Accept()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO: Handle shutdown more gracefully.
|
// TODO: Handle shutdown more gracefully?
|
||||||
logger.Errorf("Failed to accept connection, aborting loop: %v", err)
|
logger.Errorf("Failed to accept connection, aborting loop: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// From a standard TCP connection to an encrypted SSH connection
|
// Goroutineify to resume accepting sockets early.
|
||||||
sshConn, channels, requests, err := ssh.NewServerConn(conn, s.sshConfig)
|
go func() {
|
||||||
if err != nil {
|
// From a standard TCP connection to an encrypted SSH connection
|
||||||
logger.Errorf("Failed to handshake: %v", err)
|
sshConn, channels, requests, err := ssh.NewServerConn(conn, s.sshConfig)
|
||||||
continue
|
if err != nil {
|
||||||
}
|
logger.Errorf("Failed to handshake: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
logger.Infof("Connection from: %s, %s, %s", sshConn.RemoteAddr(), sshConn.User(), sshConn.ClientVersion())
|
logger.Infof("Connection from: %s, %s, %s", sshConn.RemoteAddr(), sshConn.User(), sshConn.ClientVersion())
|
||||||
|
|
||||||
go ssh.DiscardRequests(requests)
|
go ssh.DiscardRequests(requests)
|
||||||
go s.handleChannels(channels)
|
|
||||||
|
client := NewClient(s, sshConn.User())
|
||||||
|
// TODO: mutex this
|
||||||
|
|
||||||
|
s.lock.Lock()
|
||||||
|
s.clients = append(s.clients, *client)
|
||||||
|
s.lock.Unlock()
|
||||||
|
|
||||||
|
s.Broadcast(fmt.Sprintf("* Joined: %s", client.Name))
|
||||||
|
|
||||||
|
go client.handleChannels(channels)
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return s.done, nil
|
go func() {
|
||||||
|
<-s.done
|
||||||
|
socket.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Stop() error {
|
func (s *Server) Stop() {
|
||||||
err := (*s.socket).Close()
|
close(s.done)
|
||||||
s.done <- struct{}{}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user