Root sshchat package, main moved into cmd/ssh-chat/.
authorAndrey Petrov <andrey.petrov@shazow.net>
Wed, 21 Jan 2015 19:47:59 +0000 (11:47 -0800)
committerAndrey Petrov <andrey.petrov@shazow.net>
Wed, 21 Jan 2015 19:47:59 +0000 (11:47 -0800)
12 files changed:
Makefile
auth.go
auth_test.go
cmd/ssh-chat/cmd.go [moved from cmd.go with 84% similarity]
cmd/ssh-chat/key.go [moved from key.go with 100% similarity]
godoc.go [new file with mode: 0644]
host.go
host_test.go
identity.go
logger.go
set.go
set_test.go

index a2b03dbc4d6920f2168b325e892f6340314dbfbf..4ac14003e86434cde5274e62bf50213363eb962b 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2,13 +2,12 @@ BINARY = ssh-chat
 KEY = host_key
 PORT = 2022
 
-all: $(BINARY)
+SRCS = %.go
 
-**/*.go:
-       go build ./...
+all: $(BINARY)
 
-$(BINARY): **/*.go *.go
-       go build -ldflags "-X main.buildCommit `git describe --long --tags --dirty --always`" .
+$(BINARY): **/**/*.go **/*.go *.go
+       go build -ldflags "-X main.buildCommit `git describe --long --tags --dirty --always`" ./cmd/ssh-chat
 
 deps:
        go get .
diff --git a/auth.go b/auth.go
index 8c86b265a5b2d5bcf46f2533e30ab7c29f1bd50d..e8f919268723fcff683bcb0c2b7ee244c8d61127 100644 (file)
--- a/auth.go
+++ b/auth.go
@@ -1,4 +1,4 @@
-package main
+package sshchat
 
 import (
        "errors"
@@ -16,8 +16,8 @@ var ErrNotWhitelisted = errors.New("not whitelisted")
 // The error returned a key is checked that is banned.
 var ErrBanned = errors.New("banned")
 
-// NewAuthKey returns string from an ssh.PublicKey.
-func NewAuthKey(key ssh.PublicKey) string {
+// newAuthKey returns string from an ssh.PublicKey used to index the key in our lookup.
+func newAuthKey(key ssh.PublicKey) string {
        if key == nil {
                return ""
        }
@@ -25,8 +25,8 @@ func NewAuthKey(key ssh.PublicKey) string {
        return sshd.Fingerprint(key)
 }
 
-// NewAuthAddr returns a string from a net.Addr
-func NewAuthAddr(addr net.Addr) string {
+// newAuthAddr returns a string from a net.Addr used to index the address the key in our lookup.
+func newAuthAddr(addr net.Addr) string {
        if addr == nil {
                return ""
        }
@@ -34,8 +34,7 @@ func NewAuthAddr(addr net.Addr) string {
        return host
 }
 
-// Auth stores fingerprint lookups
-// TODO: Add timed auth by using a time.Time instead of struct{} for values.
+// Auth stores lookups for bans, whitelists, and ops. It implements the sshd.Auth interface.
 type Auth struct {
        sync.RWMutex
        bannedAddr *Set
@@ -44,7 +43,7 @@ type Auth struct {
        ops        *Set
 }
 
-// NewAuth creates a new default Auth.
+// NewAuth creates a new empty Auth.
 func NewAuth() *Auth {
        return &Auth{
                bannedAddr: NewSet(),
@@ -61,7 +60,7 @@ func (a Auth) AllowAnonymous() bool {
 
 // Check determines if a pubkey fingerprint is permitted.
 func (a *Auth) Check(addr net.Addr, key ssh.PublicKey) (bool, error) {
-       authkey := NewAuthKey(key)
+       authkey := newAuthKey(key)
 
        if a.whitelist.Len() != 0 {
                // Only check whitelist if there is something in it, otherwise it's disabled.
@@ -74,7 +73,7 @@ func (a *Auth) Check(addr net.Addr, key ssh.PublicKey) (bool, error) {
 
        banned := a.banned.In(authkey)
        if !banned {
-               banned = a.bannedAddr.In(NewAuthAddr(addr))
+               banned = a.bannedAddr.In(newAuthAddr(addr))
        }
        if banned {
                return false, ErrBanned
@@ -88,7 +87,7 @@ func (a *Auth) Op(key ssh.PublicKey, d time.Duration) {
        if key == nil {
                return
        }
-       authkey := NewAuthKey(key)
+       authkey := newAuthKey(key)
        if d != 0 {
                a.ops.AddExpiring(authkey, d)
        } else {
@@ -102,7 +101,7 @@ func (a *Auth) IsOp(key ssh.PublicKey) bool {
        if key == nil {
                return false
        }
-       authkey := NewAuthKey(key)
+       authkey := newAuthKey(key)
        return a.ops.In(authkey)
 }
 
@@ -111,7 +110,7 @@ func (a *Auth) Whitelist(key ssh.PublicKey, d time.Duration) {
        if key == nil {
                return
        }
-       authkey := NewAuthKey(key)
+       authkey := newAuthKey(key)
        if d != 0 {
                a.whitelist.AddExpiring(authkey, d)
        } else {
@@ -125,7 +124,7 @@ func (a *Auth) Ban(key ssh.PublicKey, d time.Duration) {
        if key == nil {
                return
        }
-       a.BanFingerprint(NewAuthKey(key), d)
+       a.BanFingerprint(newAuthKey(key), d)
 }
 
 // BanFingerprint will set a public key fingerprint as banned.
@@ -140,7 +139,7 @@ func (a *Auth) BanFingerprint(authkey string, d time.Duration) {
 
 // Ban will set an IP address as banned.
 func (a *Auth) BanAddr(addr net.Addr, d time.Duration) {
-       key := NewAuthAddr(addr)
+       key := newAuthAddr(addr)
        if d != 0 {
                a.bannedAddr.AddExpiring(key, d)
        } else {
index 981a1d68854273651a5208824d648ffbbf11908f..46925574644902969f9af5f6be7bc4174873d39d 100644 (file)
@@ -1,4 +1,4 @@
-package main
+package sshchat
 
 import (
        "crypto/rand"
similarity index 84%
rename from cmd.go
rename to cmd/ssh-chat/cmd.go
index 55646fc08f30e9bdeb75e230466d01fbab77e22b..0c0fb99608655326fc5d23e9b76cad22011c6e7a 100644 (file)
--- a/cmd.go
@@ -15,6 +15,7 @@ import (
        "github.com/jessevdk/go-flags"
        "golang.org/x/crypto/ssh"
 
+       "github.com/shazow/ssh-chat"
        "github.com/shazow/ssh-chat/chat"
        "github.com/shazow/ssh-chat/chat/message"
        "github.com/shazow/ssh-chat/sshd"
@@ -39,7 +40,10 @@ var logLevels = []log.Level{
        log.Debug,
 }
 
-var buildCommit string
+func fail(code int, format string, args ...interface{}) {
+       fmt.Fprintf(os.Stderr, format, args...)
+       os.Exit(code)
+}
 
 func main() {
        options := Options{}
@@ -66,7 +70,7 @@ func main() {
        }
 
        logLevel := logLevels[numVerbose]
-       logger = golog.New(os.Stderr, logLevel)
+       sshchat.SetLogger(golog.New(os.Stderr, logLevel))
 
        if logLevel == log.Debug {
                // Enable logging from submodules
@@ -84,33 +88,29 @@ func main() {
 
        privateKey, err := ReadPrivateKey(privateKeyPath)
        if err != nil {
-               logger.Errorf("Couldn't read private key: %v", err)
-               os.Exit(2)
+               fail(2, "Couldn't read private key: %v\n", err)
        }
 
        signer, err := ssh.ParsePrivateKey(privateKey)
        if err != nil {
-               logger.Errorf("Failed to parse key: %v", err)
-               os.Exit(3)
+               fail(3, "Failed to parse key: %v\n", err)
        }
 
-       auth := NewAuth()
+       auth := sshchat.NewAuth()
        config := sshd.MakeAuth(auth)
        config.AddHostKey(signer)
 
        s, err := sshd.ListenSSH(options.Bind, config)
        if err != nil {
-               logger.Errorf("Failed to listen on socket: %v", err)
-               os.Exit(4)
+               fail(4, "Failed to listen on socket: %v\n", err)
        }
        defer s.Close()
        s.RateLimit = true
 
        fmt.Printf("Listening for connections on %v\n", s.Addr().String())
 
-       host := NewHost(s)
-       host.auth = auth
-       host.theme = &message.Themes[0]
+       host := sshchat.NewHost(s, auth)
+       host.SetTheme(message.Themes[0])
 
        err = fromFile(options.Admin, func(line []byte) error {
                key, _, _, _, err := ssh.ParseAuthorizedKey(line)
@@ -121,8 +121,7 @@ func main() {
                return nil
        })
        if err != nil {
-               logger.Errorf("Failed to load admins: %v", err)
-               os.Exit(5)
+               fail(5, "Failed to load admins: %v\n", err)
        }
 
        err = fromFile(options.Whitelist, func(line []byte) error {
@@ -131,19 +130,16 @@ func main() {
                        return err
                }
                auth.Whitelist(key, 0)
-               logger.Debugf("Whitelisted: %s", line)
                return nil
        })
        if err != nil {
-               logger.Errorf("Failed to load whitelist: %v", err)
-               os.Exit(5)
+               fail(6, "Failed to load whitelist: %v\n", err)
        }
 
        if options.Motd != "" {
                motd, err := ioutil.ReadFile(options.Motd)
                if err != nil {
-                       logger.Errorf("Failed to load MOTD file: %v", err)
-                       return
+                       fail(7, "Failed to load MOTD file: %v\n", err)
                }
                motdString := strings.TrimSpace(string(motd))
                // hack to normalize line endings into \r\n
@@ -157,8 +153,7 @@ func main() {
        } else if options.Log != "" {
                fp, err := os.OpenFile(options.Log, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
                if err != nil {
-                       logger.Errorf("Failed to open log file for writing: %v", err)
-                       return
+                       fail(8, "Failed to open log file for writing: %v", err)
                }
                host.SetLogging(fp)
        }
@@ -170,7 +165,7 @@ func main() {
        signal.Notify(sig, os.Interrupt)
 
        <-sig // Wait for ^C signal
-       logger.Warningf("Interrupt signal detected, shutting down.")
+       fmt.Fprintln(os.Stderr, "Interrupt signal detected, shutting down.")
        os.Exit(0)
 }
 
similarity index 100%
rename from key.go
rename to cmd/ssh-chat/key.go
diff --git a/godoc.go b/godoc.go
new file mode 100644 (file)
index 0000000..8b4842b
--- /dev/null
+++ b/godoc.go
@@ -0,0 +1,11 @@
+/*
+sshchat package is an implementation of an ssh server which serves a chat room
+instead of a shell.
+
+sshd subdirectory contains the ssh-related pieces which know nothing about chat.
+
+chat subdirectory contains the chat-related pieces which know nothing about ssh.
+
+The Host type is the glue between the sshd and chat pieces.
+*/
+package sshchat
diff --git a/host.go b/host.go
index aab7843c5475ef34015ac28c04549ccb57a27424..ddaa260f638f57d13778e6c75942fe20527d83c1 100644 (file)
--- a/host.go
+++ b/host.go
@@ -1,4 +1,4 @@
-package main
+package sshchat
 
 import (
        "errors"
@@ -13,6 +13,8 @@ import (
        "github.com/shazow/ssh-chat/sshd"
 )
 
+var buildCommit string
+
 const maxInputLength int = 1024
 
 // GetPrompt will render the terminal prompt string based on the user.
@@ -36,16 +38,17 @@ type Host struct {
        count int
 
        // Default theme
-       theme *message.Theme
+       theme message.Theme
 }
 
 // NewHost creates a Host on top of an existing listener.
-func NewHost(listener *sshd.SSHListener) *Host {
+func NewHost(listener *sshd.SSHListener, auth *Auth) *Host {
        room := chat.NewRoom()
        h := Host{
                Room:     room,
                listener: listener,
                commands: chat.Commands{},
+               auth:     auth,
        }
 
        // Make our own commands registry instance.
@@ -57,6 +60,11 @@ func NewHost(listener *sshd.SSHListener) *Host {
        return &h
 }
 
+// SetTheme sets the default theme for the host.
+func (h *Host) SetTheme(theme message.Theme) {
+       h.theme = theme
+}
+
 // SetMotd sets the host's message of the day.
 func (h *Host) SetMotd(motd string) {
        h.motd = motd
@@ -74,7 +82,7 @@ func (h Host) isOp(conn sshd.Connection) bool {
 func (h *Host) Connect(term *sshd.Terminal) {
        id := NewIdentity(term.Conn)
        user := message.NewUserScreen(id, term)
-       user.Config.Theme = h.theme
+       user.Config.Theme = &h.theme
        go func() {
                // Close term once user is closed.
                user.Wait()
index 0288ab2fe77c5abdb1ea5dd0e54e9d1b04355248..f804f2888729f3e428ceaba2444374c9c427a729 100644 (file)
@@ -1,4 +1,4 @@
-package main
+package sshchat
 
 import (
        "bufio"
@@ -56,7 +56,7 @@ func TestHostNameCollision(t *testing.T) {
                t.Fatal(err)
        }
        defer s.Close()
-       host := NewHost(s)
+       host := NewHost(s, nil)
        go host.Serve()
 
        done := make(chan struct{}, 1)
@@ -70,7 +70,7 @@ func TestHostNameCollision(t *testing.T) {
                        scanner.Scan()
                        actual := scanner.Text()
                        if !strings.HasPrefix(actual, "[foo] ") {
-                               t.Errorf("First client failed to get 'foo' name.")
+                               t.Errorf("First client failed to get 'foo' name: %q", actual)
                        }
 
                        actual = stripPrompt(actual)
@@ -133,8 +133,7 @@ func TestHostWhitelist(t *testing.T) {
                t.Fatal(err)
        }
        defer s.Close()
-       host := NewHost(s)
-       host.auth = auth
+       host := NewHost(s, auth)
        go host.Serve()
 
        target := s.Addr().String()
@@ -174,7 +173,7 @@ func TestHostKick(t *testing.T) {
        }
        defer s.Close()
        addr := s.Addr().String()
-       host := NewHost(s)
+       host := NewHost(s, nil)
        go host.Serve()
 
        connected := make(chan struct{})
index 4403d8b2edb79d7c4d0b42b1ff7c7dca14d0559e..21988e348b9f13e9f052333a02b6badfc6c3d979 100644 (file)
@@ -1,4 +1,4 @@
-package main
+package sshchat
 
 import (
        "fmt"
index 4fabd05b99eb0e822fa8f499d34495390c7649db..d1c64acf50db22873714f3a61f52904559a56e34 100644 (file)
--- a/logger.go
+++ b/logger.go
@@ -1,4 +1,4 @@
-package main
+package sshchat
 
 import (
        "bytes"
@@ -9,8 +9,12 @@ import (
 
 var logger *golog.Logger
 
+func SetLogger(l *golog.Logger) {
+       logger = l
+}
+
 func init() {
        // Set a default null logger
        var b bytes.Buffer
-       logger = golog.New(&b, log.Debug)
+       SetLogger(golog.New(&b, log.Debug))
 }
diff --git a/set.go b/set.go
index 86afe133234e721c101570ea4dca01a99913db1a..f0d607c19790676fdb9312605f87cf770f1c9ddd 100644 (file)
--- a/set.go
+++ b/set.go
@@ -1,4 +1,4 @@
-package main
+package sshchat
 
 import (
        "sync"
@@ -19,20 +19,20 @@ func (v value) Bool() bool {
        return true
 }
 
-type SetValue interface {
+type setValue interface {
        Bool() bool
 }
 
 // Set with expire-able keys
 type Set struct {
-       lookup map[string]SetValue
+       lookup map[string]setValue
        sync.Mutex
 }
 
 // NewSet creates a new set.
 func NewSet() *Set {
        return &Set{
-               lookup: map[string]SetValue{},
+               lookup: map[string]setValue{},
        }
 }
 
index 0a4b9eae3ce0cb4a029ce2b655af0d162b00c218..1d7fbefac40d5095ae825c38f04c52e46a605ba6 100644 (file)
@@ -1,4 +1,4 @@
-package main
+package sshchat
 
 import (
        "testing"