go.crypto: initial code
Manual edits to README. Moved from main Go repository, deleted Makefiles, ran gofix -r go1rename. Tested with: go test code.google.com/p/go.crypto/... R=golang-dev, bradfitz CC=golang-dev https://golang.org/cl/5564059
This commit is contained in:
@@ -0,0 +1,374 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ssh
|
||||
|
||||
// Session tests.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"exp/terminal"
|
||||
"io"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type serverType func(*channel)
|
||||
|
||||
// dial constructs a new test server and returns a *ClientConn.
|
||||
func dial(handler serverType, t *testing.T) *ClientConn {
|
||||
pw := password("tiger")
|
||||
serverConfig.PasswordCallback = func(user, pass string) bool {
|
||||
return user == "testuser" && pass == string(pw)
|
||||
}
|
||||
serverConfig.PublicKeyCallback = nil
|
||||
|
||||
l, err := Listen("tcp", "127.0.0.1:0", serverConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to listen: %s", err)
|
||||
}
|
||||
go func() {
|
||||
defer l.Close()
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
t.Errorf("Unable to accept: %v", err)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
if err := conn.Handshake(); err != nil {
|
||||
t.Errorf("Unable to handshake: %v", err)
|
||||
return
|
||||
}
|
||||
for {
|
||||
ch, err := conn.Accept()
|
||||
if err == io.EOF {
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("Unable to accept incoming channel request: %v", err)
|
||||
return
|
||||
}
|
||||
if ch.ChannelType() != "session" {
|
||||
ch.Reject(UnknownChannelType, "unknown channel type")
|
||||
continue
|
||||
}
|
||||
ch.Accept()
|
||||
go handler(ch.(*channel))
|
||||
}
|
||||
t.Log("done")
|
||||
}()
|
||||
|
||||
config := &ClientConfig{
|
||||
User: "testuser",
|
||||
Auth: []ClientAuth{
|
||||
ClientAuthPassword(pw),
|
||||
},
|
||||
}
|
||||
|
||||
c, err := Dial("tcp", l.Addr().String(), config)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to dial remote side: %s", err)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// Test a simple string is returned to session.Stdout.
|
||||
func TestSessionShell(t *testing.T) {
|
||||
conn := dial(shellHandler, t)
|
||||
defer conn.Close()
|
||||
session, err := conn.NewSession()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to request new session: %s", err)
|
||||
}
|
||||
defer session.Close()
|
||||
stdout := new(bytes.Buffer)
|
||||
session.Stdout = stdout
|
||||
if err := session.Shell(); err != nil {
|
||||
t.Fatalf("Unable to execute command: %s", err)
|
||||
}
|
||||
if err := session.Wait(); err != nil {
|
||||
t.Fatalf("Remote command did not exit cleanly: %s", err)
|
||||
}
|
||||
actual := stdout.String()
|
||||
if actual != "golang" {
|
||||
t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(dfc) add support for Std{in,err}Pipe when the Server supports it.
|
||||
|
||||
// Test a simple string is returned via StdoutPipe.
|
||||
func TestSessionStdoutPipe(t *testing.T) {
|
||||
conn := dial(shellHandler, t)
|
||||
defer conn.Close()
|
||||
session, err := conn.NewSession()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to request new session: %s", err)
|
||||
}
|
||||
defer session.Close()
|
||||
stdout, err := session.StdoutPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to request StdoutPipe(): %v", err)
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if err := session.Shell(); err != nil {
|
||||
t.Fatalf("Unable to execute command: %s", err)
|
||||
}
|
||||
done := make(chan bool, 1)
|
||||
go func() {
|
||||
if _, err := io.Copy(&buf, stdout); err != nil {
|
||||
t.Errorf("Copy of stdout failed: %v", err)
|
||||
}
|
||||
done <- true
|
||||
}()
|
||||
if err := session.Wait(); err != nil {
|
||||
t.Fatalf("Remote command did not exit cleanly: %s", err)
|
||||
}
|
||||
<-done
|
||||
actual := buf.String()
|
||||
if actual != "golang" {
|
||||
t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual)
|
||||
}
|
||||
}
|
||||
|
||||
// Test non-0 exit status is returned correctly.
|
||||
func TestExitStatusNonZero(t *testing.T) {
|
||||
conn := dial(exitStatusNonZeroHandler, t)
|
||||
defer conn.Close()
|
||||
session, err := conn.NewSession()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to request new session: %s", err)
|
||||
}
|
||||
defer session.Close()
|
||||
if err := session.Shell(); err != nil {
|
||||
t.Fatalf("Unable to execute command: %s", err)
|
||||
}
|
||||
err = session.Wait()
|
||||
if err == nil {
|
||||
t.Fatalf("expected command to fail but it didn't")
|
||||
}
|
||||
e, ok := err.(*ExitError)
|
||||
if !ok {
|
||||
t.Fatalf("expected *ExitError but got %T", err)
|
||||
}
|
||||
if e.ExitStatus() != 15 {
|
||||
t.Fatalf("expected command to exit with 15 but got %s", e.ExitStatus())
|
||||
}
|
||||
}
|
||||
|
||||
// Test 0 exit status is returned correctly.
|
||||
func TestExitStatusZero(t *testing.T) {
|
||||
conn := dial(exitStatusZeroHandler, t)
|
||||
defer conn.Close()
|
||||
session, err := conn.NewSession()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to request new session: %s", err)
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
if err := session.Shell(); err != nil {
|
||||
t.Fatalf("Unable to execute command: %s", err)
|
||||
}
|
||||
err = session.Wait()
|
||||
if err != nil {
|
||||
t.Fatalf("expected nil but got %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test exit signal and status are both returned correctly.
|
||||
func TestExitSignalAndStatus(t *testing.T) {
|
||||
conn := dial(exitSignalAndStatusHandler, t)
|
||||
defer conn.Close()
|
||||
session, err := conn.NewSession()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to request new session: %s", err)
|
||||
}
|
||||
defer session.Close()
|
||||
if err := session.Shell(); err != nil {
|
||||
t.Fatalf("Unable to execute command: %s", err)
|
||||
}
|
||||
err = session.Wait()
|
||||
if err == nil {
|
||||
t.Fatalf("expected command to fail but it didn't")
|
||||
}
|
||||
e, ok := err.(*ExitError)
|
||||
if !ok {
|
||||
t.Fatalf("expected *ExitError but got %T", err)
|
||||
}
|
||||
if e.Signal() != "TERM" || e.ExitStatus() != 15 {
|
||||
t.Fatalf("expected command to exit with signal TERM and status 15 but got signal %s and status %v", e.Signal(), e.ExitStatus())
|
||||
}
|
||||
}
|
||||
|
||||
// Test exit signal and status are both returned correctly.
|
||||
func TestKnownExitSignalOnly(t *testing.T) {
|
||||
conn := dial(exitSignalHandler, t)
|
||||
defer conn.Close()
|
||||
session, err := conn.NewSession()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to request new session: %s", err)
|
||||
}
|
||||
defer session.Close()
|
||||
if err := session.Shell(); err != nil {
|
||||
t.Fatalf("Unable to execute command: %s", err)
|
||||
}
|
||||
err = session.Wait()
|
||||
if err == nil {
|
||||
t.Fatalf("expected command to fail but it didn't")
|
||||
}
|
||||
e, ok := err.(*ExitError)
|
||||
if !ok {
|
||||
t.Fatalf("expected *ExitError but got %T", err)
|
||||
}
|
||||
if e.Signal() != "TERM" || e.ExitStatus() != 143 {
|
||||
t.Fatalf("expected command to exit with signal TERM and status 143 but got signal %s and status %v", e.Signal(), e.ExitStatus())
|
||||
}
|
||||
}
|
||||
|
||||
// Test exit signal and status are both returned correctly.
|
||||
func TestUnknownExitSignal(t *testing.T) {
|
||||
conn := dial(exitSignalUnknownHandler, t)
|
||||
defer conn.Close()
|
||||
session, err := conn.NewSession()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to request new session: %s", err)
|
||||
}
|
||||
defer session.Close()
|
||||
if err := session.Shell(); err != nil {
|
||||
t.Fatalf("Unable to execute command: %s", err)
|
||||
}
|
||||
err = session.Wait()
|
||||
if err == nil {
|
||||
t.Fatalf("expected command to fail but it didn't")
|
||||
}
|
||||
e, ok := err.(*ExitError)
|
||||
if !ok {
|
||||
t.Fatalf("expected *ExitError but got %T", err)
|
||||
}
|
||||
if e.Signal() != "SYS" || e.ExitStatus() != 128 {
|
||||
t.Fatalf("expected command to exit with signal SYS and status 128 but got signal %s and status %v", e.Signal(), e.ExitStatus())
|
||||
}
|
||||
}
|
||||
|
||||
// Test WaitMsg is not returned if the channel closes abruptly.
|
||||
func TestExitWithoutStatusOrSignal(t *testing.T) {
|
||||
conn := dial(exitWithoutSignalOrStatus, t)
|
||||
defer conn.Close()
|
||||
session, err := conn.NewSession()
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to request new session: %s", err)
|
||||
}
|
||||
defer session.Close()
|
||||
if err := session.Shell(); err != nil {
|
||||
t.Fatalf("Unable to execute command: %s", err)
|
||||
}
|
||||
err = session.Wait()
|
||||
if err == nil {
|
||||
t.Fatalf("expected command to fail but it didn't")
|
||||
}
|
||||
_, ok := err.(*ExitError)
|
||||
if ok {
|
||||
// you can't actually test for errors.errorString
|
||||
// because it's not exported.
|
||||
t.Fatalf("expected *errorString but got %T", err)
|
||||
}
|
||||
}
|
||||
|
||||
type exitStatusMsg struct {
|
||||
PeersId uint32
|
||||
Request string
|
||||
WantReply bool
|
||||
Status uint32
|
||||
}
|
||||
|
||||
type exitSignalMsg struct {
|
||||
PeersId uint32
|
||||
Request string
|
||||
WantReply bool
|
||||
Signal string
|
||||
CoreDumped bool
|
||||
Errmsg string
|
||||
Lang string
|
||||
}
|
||||
|
||||
func newServerShell(ch *channel, prompt string) *ServerTerminal {
|
||||
term := terminal.NewTerminal(ch, prompt)
|
||||
return &ServerTerminal{
|
||||
Term: term,
|
||||
Channel: ch,
|
||||
}
|
||||
}
|
||||
|
||||
func exitStatusZeroHandler(ch *channel) {
|
||||
defer ch.Close()
|
||||
// this string is returned to stdout
|
||||
shell := newServerShell(ch, "> ")
|
||||
shell.ReadLine()
|
||||
sendStatus(0, ch)
|
||||
}
|
||||
|
||||
func exitStatusNonZeroHandler(ch *channel) {
|
||||
defer ch.Close()
|
||||
shell := newServerShell(ch, "> ")
|
||||
shell.ReadLine()
|
||||
sendStatus(15, ch)
|
||||
}
|
||||
|
||||
func exitSignalAndStatusHandler(ch *channel) {
|
||||
defer ch.Close()
|
||||
shell := newServerShell(ch, "> ")
|
||||
shell.ReadLine()
|
||||
sendStatus(15, ch)
|
||||
sendSignal("TERM", ch)
|
||||
}
|
||||
|
||||
func exitSignalHandler(ch *channel) {
|
||||
defer ch.Close()
|
||||
shell := newServerShell(ch, "> ")
|
||||
shell.ReadLine()
|
||||
sendSignal("TERM", ch)
|
||||
}
|
||||
|
||||
func exitSignalUnknownHandler(ch *channel) {
|
||||
defer ch.Close()
|
||||
shell := newServerShell(ch, "> ")
|
||||
shell.ReadLine()
|
||||
sendSignal("SYS", ch)
|
||||
}
|
||||
|
||||
func exitWithoutSignalOrStatus(ch *channel) {
|
||||
defer ch.Close()
|
||||
shell := newServerShell(ch, "> ")
|
||||
shell.ReadLine()
|
||||
}
|
||||
|
||||
func shellHandler(ch *channel) {
|
||||
defer ch.Close()
|
||||
// this string is returned to stdout
|
||||
shell := newServerShell(ch, "golang")
|
||||
shell.ReadLine()
|
||||
sendStatus(0, ch)
|
||||
}
|
||||
|
||||
func sendStatus(status uint32, ch *channel) {
|
||||
msg := exitStatusMsg{
|
||||
PeersId: ch.theirId,
|
||||
Request: "exit-status",
|
||||
WantReply: false,
|
||||
Status: status,
|
||||
}
|
||||
ch.serverConn.writePacket(marshal(msgChannelRequest, msg))
|
||||
}
|
||||
|
||||
func sendSignal(signal string, ch *channel) {
|
||||
sig := exitSignalMsg{
|
||||
PeersId: ch.theirId,
|
||||
Request: "exit-signal",
|
||||
WantReply: false,
|
||||
Signal: signal,
|
||||
CoreDumped: false,
|
||||
Errmsg: "Process terminated",
|
||||
Lang: "en-GB-oed",
|
||||
}
|
||||
ch.serverConn.writePacket(marshal(msgChannelRequest, sig))
|
||||
}
|
||||
Reference in New Issue
Block a user