github.com/deis/deis@v1.13.5-0.20170519182049-1d9e59fbdbfc/Godeps/_workspace/src/golang.org/x/crypto/ssh/session_test.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package ssh 6 7 // Session tests. 8 9 import ( 10 "bytes" 11 crypto_rand "crypto/rand" 12 "io" 13 "io/ioutil" 14 "math/rand" 15 "testing" 16 17 "golang.org/x/crypto/ssh/terminal" 18 ) 19 20 type serverType func(Channel, <-chan *Request, *testing.T) 21 22 // dial constructs a new test server and returns a *ClientConn. 23 func dial(handler serverType, t *testing.T) *Client { 24 c1, c2, err := netPipe() 25 if err != nil { 26 t.Fatalf("netPipe: %v", err) 27 } 28 29 go func() { 30 defer c1.Close() 31 conf := ServerConfig{ 32 NoClientAuth: true, 33 } 34 conf.AddHostKey(testSigners["rsa"]) 35 36 _, chans, reqs, err := NewServerConn(c1, &conf) 37 if err != nil { 38 t.Fatalf("Unable to handshake: %v", err) 39 } 40 go DiscardRequests(reqs) 41 42 for newCh := range chans { 43 if newCh.ChannelType() != "session" { 44 newCh.Reject(UnknownChannelType, "unknown channel type") 45 continue 46 } 47 48 ch, inReqs, err := newCh.Accept() 49 if err != nil { 50 t.Errorf("Accept: %v", err) 51 continue 52 } 53 go func() { 54 handler(ch, inReqs, t) 55 }() 56 } 57 }() 58 59 config := &ClientConfig{ 60 User: "testuser", 61 } 62 63 conn, chans, reqs, err := NewClientConn(c2, "", config) 64 if err != nil { 65 t.Fatalf("unable to dial remote side: %v", err) 66 } 67 68 return NewClient(conn, chans, reqs) 69 } 70 71 // Test a simple string is returned to session.Stdout. 72 func TestSessionShell(t *testing.T) { 73 conn := dial(shellHandler, t) 74 defer conn.Close() 75 session, err := conn.NewSession() 76 if err != nil { 77 t.Fatalf("Unable to request new session: %v", err) 78 } 79 defer session.Close() 80 stdout := new(bytes.Buffer) 81 session.Stdout = stdout 82 if err := session.Shell(); err != nil { 83 t.Fatalf("Unable to execute command: %s", err) 84 } 85 if err := session.Wait(); err != nil { 86 t.Fatalf("Remote command did not exit cleanly: %v", err) 87 } 88 actual := stdout.String() 89 if actual != "golang" { 90 t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual) 91 } 92 } 93 94 // TODO(dfc) add support for Std{in,err}Pipe when the Server supports it. 95 96 // Test a simple string is returned via StdoutPipe. 97 func TestSessionStdoutPipe(t *testing.T) { 98 conn := dial(shellHandler, t) 99 defer conn.Close() 100 session, err := conn.NewSession() 101 if err != nil { 102 t.Fatalf("Unable to request new session: %v", err) 103 } 104 defer session.Close() 105 stdout, err := session.StdoutPipe() 106 if err != nil { 107 t.Fatalf("Unable to request StdoutPipe(): %v", err) 108 } 109 var buf bytes.Buffer 110 if err := session.Shell(); err != nil { 111 t.Fatalf("Unable to execute command: %v", err) 112 } 113 done := make(chan bool, 1) 114 go func() { 115 if _, err := io.Copy(&buf, stdout); err != nil { 116 t.Errorf("Copy of stdout failed: %v", err) 117 } 118 done <- true 119 }() 120 if err := session.Wait(); err != nil { 121 t.Fatalf("Remote command did not exit cleanly: %v", err) 122 } 123 <-done 124 actual := buf.String() 125 if actual != "golang" { 126 t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual) 127 } 128 } 129 130 // Test that a simple string is returned via the Output helper, 131 // and that stderr is discarded. 132 func TestSessionOutput(t *testing.T) { 133 conn := dial(fixedOutputHandler, t) 134 defer conn.Close() 135 session, err := conn.NewSession() 136 if err != nil { 137 t.Fatalf("Unable to request new session: %v", err) 138 } 139 defer session.Close() 140 141 buf, err := session.Output("") // cmd is ignored by fixedOutputHandler 142 if err != nil { 143 t.Error("Remote command did not exit cleanly:", err) 144 } 145 w := "this-is-stdout." 146 g := string(buf) 147 if g != w { 148 t.Error("Remote command did not return expected string:") 149 t.Logf("want %q", w) 150 t.Logf("got %q", g) 151 } 152 } 153 154 // Test that both stdout and stderr are returned 155 // via the CombinedOutput helper. 156 func TestSessionCombinedOutput(t *testing.T) { 157 conn := dial(fixedOutputHandler, t) 158 defer conn.Close() 159 session, err := conn.NewSession() 160 if err != nil { 161 t.Fatalf("Unable to request new session: %v", err) 162 } 163 defer session.Close() 164 165 buf, err := session.CombinedOutput("") // cmd is ignored by fixedOutputHandler 166 if err != nil { 167 t.Error("Remote command did not exit cleanly:", err) 168 } 169 const stdout = "this-is-stdout." 170 const stderr = "this-is-stderr." 171 g := string(buf) 172 if g != stdout+stderr && g != stderr+stdout { 173 t.Error("Remote command did not return expected string:") 174 t.Logf("want %q, or %q", stdout+stderr, stderr+stdout) 175 t.Logf("got %q", g) 176 } 177 } 178 179 // Test non-0 exit status is returned correctly. 180 func TestExitStatusNonZero(t *testing.T) { 181 conn := dial(exitStatusNonZeroHandler, t) 182 defer conn.Close() 183 session, err := conn.NewSession() 184 if err != nil { 185 t.Fatalf("Unable to request new session: %v", err) 186 } 187 defer session.Close() 188 if err := session.Shell(); err != nil { 189 t.Fatalf("Unable to execute command: %v", err) 190 } 191 err = session.Wait() 192 if err == nil { 193 t.Fatalf("expected command to fail but it didn't") 194 } 195 e, ok := err.(*ExitError) 196 if !ok { 197 t.Fatalf("expected *ExitError but got %T", err) 198 } 199 if e.ExitStatus() != 15 { 200 t.Fatalf("expected command to exit with 15 but got %v", e.ExitStatus()) 201 } 202 } 203 204 // Test 0 exit status is returned correctly. 205 func TestExitStatusZero(t *testing.T) { 206 conn := dial(exitStatusZeroHandler, t) 207 defer conn.Close() 208 session, err := conn.NewSession() 209 if err != nil { 210 t.Fatalf("Unable to request new session: %v", err) 211 } 212 defer session.Close() 213 214 if err := session.Shell(); err != nil { 215 t.Fatalf("Unable to execute command: %v", err) 216 } 217 err = session.Wait() 218 if err != nil { 219 t.Fatalf("expected nil but got %v", err) 220 } 221 } 222 223 // Test exit signal and status are both returned correctly. 224 func TestExitSignalAndStatus(t *testing.T) { 225 conn := dial(exitSignalAndStatusHandler, t) 226 defer conn.Close() 227 session, err := conn.NewSession() 228 if err != nil { 229 t.Fatalf("Unable to request new session: %v", err) 230 } 231 defer session.Close() 232 if err := session.Shell(); err != nil { 233 t.Fatalf("Unable to execute command: %v", err) 234 } 235 err = session.Wait() 236 if err == nil { 237 t.Fatalf("expected command to fail but it didn't") 238 } 239 e, ok := err.(*ExitError) 240 if !ok { 241 t.Fatalf("expected *ExitError but got %T", err) 242 } 243 if e.Signal() != "TERM" || e.ExitStatus() != 15 { 244 t.Fatalf("expected command to exit with signal TERM and status 15 but got signal %s and status %v", e.Signal(), e.ExitStatus()) 245 } 246 } 247 248 // Test exit signal and status are both returned correctly. 249 func TestKnownExitSignalOnly(t *testing.T) { 250 conn := dial(exitSignalHandler, t) 251 defer conn.Close() 252 session, err := conn.NewSession() 253 if err != nil { 254 t.Fatalf("Unable to request new session: %v", err) 255 } 256 defer session.Close() 257 if err := session.Shell(); err != nil { 258 t.Fatalf("Unable to execute command: %v", err) 259 } 260 err = session.Wait() 261 if err == nil { 262 t.Fatalf("expected command to fail but it didn't") 263 } 264 e, ok := err.(*ExitError) 265 if !ok { 266 t.Fatalf("expected *ExitError but got %T", err) 267 } 268 if e.Signal() != "TERM" || e.ExitStatus() != 143 { 269 t.Fatalf("expected command to exit with signal TERM and status 143 but got signal %s and status %v", e.Signal(), e.ExitStatus()) 270 } 271 } 272 273 // Test exit signal and status are both returned correctly. 274 func TestUnknownExitSignal(t *testing.T) { 275 conn := dial(exitSignalUnknownHandler, t) 276 defer conn.Close() 277 session, err := conn.NewSession() 278 if err != nil { 279 t.Fatalf("Unable to request new session: %v", err) 280 } 281 defer session.Close() 282 if err := session.Shell(); err != nil { 283 t.Fatalf("Unable to execute command: %v", err) 284 } 285 err = session.Wait() 286 if err == nil { 287 t.Fatalf("expected command to fail but it didn't") 288 } 289 e, ok := err.(*ExitError) 290 if !ok { 291 t.Fatalf("expected *ExitError but got %T", err) 292 } 293 if e.Signal() != "SYS" || e.ExitStatus() != 128 { 294 t.Fatalf("expected command to exit with signal SYS and status 128 but got signal %s and status %v", e.Signal(), e.ExitStatus()) 295 } 296 } 297 298 // Test WaitMsg is not returned if the channel closes abruptly. 299 func TestExitWithoutStatusOrSignal(t *testing.T) { 300 conn := dial(exitWithoutSignalOrStatus, t) 301 defer conn.Close() 302 session, err := conn.NewSession() 303 if err != nil { 304 t.Fatalf("Unable to request new session: %v", err) 305 } 306 defer session.Close() 307 if err := session.Shell(); err != nil { 308 t.Fatalf("Unable to execute command: %v", err) 309 } 310 err = session.Wait() 311 if err == nil { 312 t.Fatalf("expected command to fail but it didn't") 313 } 314 _, ok := err.(*ExitError) 315 if ok { 316 // you can't actually test for errors.errorString 317 // because it's not exported. 318 t.Fatalf("expected *errorString but got %T", err) 319 } 320 } 321 322 // windowTestBytes is the number of bytes that we'll send to the SSH server. 323 const windowTestBytes = 16000 * 200 324 325 // TestServerWindow writes random data to the server. The server is expected to echo 326 // the same data back, which is compared against the original. 327 func TestServerWindow(t *testing.T) { 328 origBuf := bytes.NewBuffer(make([]byte, 0, windowTestBytes)) 329 io.CopyN(origBuf, crypto_rand.Reader, windowTestBytes) 330 origBytes := origBuf.Bytes() 331 332 conn := dial(echoHandler, t) 333 defer conn.Close() 334 session, err := conn.NewSession() 335 if err != nil { 336 t.Fatal(err) 337 } 338 defer session.Close() 339 result := make(chan []byte) 340 341 go func() { 342 defer close(result) 343 echoedBuf := bytes.NewBuffer(make([]byte, 0, windowTestBytes)) 344 serverStdout, err := session.StdoutPipe() 345 if err != nil { 346 t.Errorf("StdoutPipe failed: %v", err) 347 return 348 } 349 n, err := copyNRandomly("stdout", echoedBuf, serverStdout, windowTestBytes) 350 if err != nil && err != io.EOF { 351 t.Errorf("Read only %d bytes from server, expected %d: %v", n, windowTestBytes, err) 352 } 353 result <- echoedBuf.Bytes() 354 }() 355 356 serverStdin, err := session.StdinPipe() 357 if err != nil { 358 t.Fatalf("StdinPipe failed: %v", err) 359 } 360 written, err := copyNRandomly("stdin", serverStdin, origBuf, windowTestBytes) 361 if err != nil { 362 t.Fatalf("failed to copy origBuf to serverStdin: %v", err) 363 } 364 if written != windowTestBytes { 365 t.Fatalf("Wrote only %d of %d bytes to server", written, windowTestBytes) 366 } 367 368 echoedBytes := <-result 369 370 if !bytes.Equal(origBytes, echoedBytes) { 371 t.Fatalf("Echoed buffer differed from original, orig %d, echoed %d", len(origBytes), len(echoedBytes)) 372 } 373 } 374 375 // Verify the client can handle a keepalive packet from the server. 376 func TestClientHandlesKeepalives(t *testing.T) { 377 conn := dial(channelKeepaliveSender, t) 378 defer conn.Close() 379 session, err := conn.NewSession() 380 if err != nil { 381 t.Fatal(err) 382 } 383 defer session.Close() 384 if err := session.Shell(); err != nil { 385 t.Fatalf("Unable to execute command: %v", err) 386 } 387 err = session.Wait() 388 if err != nil { 389 t.Fatalf("expected nil but got: %v", err) 390 } 391 } 392 393 type exitStatusMsg struct { 394 Status uint32 395 } 396 397 type exitSignalMsg struct { 398 Signal string 399 CoreDumped bool 400 Errmsg string 401 Lang string 402 } 403 404 func handleTerminalRequests(in <-chan *Request) { 405 for req := range in { 406 ok := false 407 switch req.Type { 408 case "shell": 409 ok = true 410 if len(req.Payload) > 0 { 411 // We don't accept any commands, only the default shell. 412 ok = false 413 } 414 case "env": 415 ok = true 416 } 417 req.Reply(ok, nil) 418 } 419 } 420 421 func newServerShell(ch Channel, in <-chan *Request, prompt string) *terminal.Terminal { 422 term := terminal.NewTerminal(ch, prompt) 423 go handleTerminalRequests(in) 424 return term 425 } 426 427 func exitStatusZeroHandler(ch Channel, in <-chan *Request, t *testing.T) { 428 defer ch.Close() 429 // this string is returned to stdout 430 shell := newServerShell(ch, in, "> ") 431 readLine(shell, t) 432 sendStatus(0, ch, t) 433 } 434 435 func exitStatusNonZeroHandler(ch Channel, in <-chan *Request, t *testing.T) { 436 defer ch.Close() 437 shell := newServerShell(ch, in, "> ") 438 readLine(shell, t) 439 sendStatus(15, ch, t) 440 } 441 442 func exitSignalAndStatusHandler(ch Channel, in <-chan *Request, t *testing.T) { 443 defer ch.Close() 444 shell := newServerShell(ch, in, "> ") 445 readLine(shell, t) 446 sendStatus(15, ch, t) 447 sendSignal("TERM", ch, t) 448 } 449 450 func exitSignalHandler(ch Channel, in <-chan *Request, t *testing.T) { 451 defer ch.Close() 452 shell := newServerShell(ch, in, "> ") 453 readLine(shell, t) 454 sendSignal("TERM", ch, t) 455 } 456 457 func exitSignalUnknownHandler(ch Channel, in <-chan *Request, t *testing.T) { 458 defer ch.Close() 459 shell := newServerShell(ch, in, "> ") 460 readLine(shell, t) 461 sendSignal("SYS", ch, t) 462 } 463 464 func exitWithoutSignalOrStatus(ch Channel, in <-chan *Request, t *testing.T) { 465 defer ch.Close() 466 shell := newServerShell(ch, in, "> ") 467 readLine(shell, t) 468 } 469 470 func shellHandler(ch Channel, in <-chan *Request, t *testing.T) { 471 defer ch.Close() 472 // this string is returned to stdout 473 shell := newServerShell(ch, in, "golang") 474 readLine(shell, t) 475 sendStatus(0, ch, t) 476 } 477 478 // Ignores the command, writes fixed strings to stderr and stdout. 479 // Strings are "this-is-stdout." and "this-is-stderr.". 480 func fixedOutputHandler(ch Channel, in <-chan *Request, t *testing.T) { 481 defer ch.Close() 482 _, err := ch.Read(nil) 483 484 req, ok := <-in 485 if !ok { 486 t.Fatalf("error: expected channel request, got: %#v", err) 487 return 488 } 489 490 // ignore request, always send some text 491 req.Reply(true, nil) 492 493 _, err = io.WriteString(ch, "this-is-stdout.") 494 if err != nil { 495 t.Fatalf("error writing on server: %v", err) 496 } 497 _, err = io.WriteString(ch.Stderr(), "this-is-stderr.") 498 if err != nil { 499 t.Fatalf("error writing on server: %v", err) 500 } 501 sendStatus(0, ch, t) 502 } 503 504 func readLine(shell *terminal.Terminal, t *testing.T) { 505 if _, err := shell.ReadLine(); err != nil && err != io.EOF { 506 t.Errorf("unable to read line: %v", err) 507 } 508 } 509 510 func sendStatus(status uint32, ch Channel, t *testing.T) { 511 msg := exitStatusMsg{ 512 Status: status, 513 } 514 if _, err := ch.SendRequest("exit-status", false, Marshal(&msg)); err != nil { 515 t.Errorf("unable to send status: %v", err) 516 } 517 } 518 519 func sendSignal(signal string, ch Channel, t *testing.T) { 520 sig := exitSignalMsg{ 521 Signal: signal, 522 CoreDumped: false, 523 Errmsg: "Process terminated", 524 Lang: "en-GB-oed", 525 } 526 if _, err := ch.SendRequest("exit-signal", false, Marshal(&sig)); err != nil { 527 t.Errorf("unable to send signal: %v", err) 528 } 529 } 530 531 func discardHandler(ch Channel, t *testing.T) { 532 defer ch.Close() 533 io.Copy(ioutil.Discard, ch) 534 } 535 536 func echoHandler(ch Channel, in <-chan *Request, t *testing.T) { 537 defer ch.Close() 538 if n, err := copyNRandomly("echohandler", ch, ch, windowTestBytes); err != nil { 539 t.Errorf("short write, wrote %d, expected %d: %v ", n, windowTestBytes, err) 540 } 541 } 542 543 // copyNRandomly copies n bytes from src to dst. It uses a variable, and random, 544 // buffer size to exercise more code paths. 545 func copyNRandomly(title string, dst io.Writer, src io.Reader, n int) (int, error) { 546 var ( 547 buf = make([]byte, 32*1024) 548 written int 549 remaining = n 550 ) 551 for remaining > 0 { 552 l := rand.Intn(1 << 15) 553 if remaining < l { 554 l = remaining 555 } 556 nr, er := src.Read(buf[:l]) 557 nw, ew := dst.Write(buf[:nr]) 558 remaining -= nw 559 written += nw 560 if ew != nil { 561 return written, ew 562 } 563 if nr != nw { 564 return written, io.ErrShortWrite 565 } 566 if er != nil && er != io.EOF { 567 return written, er 568 } 569 } 570 return written, nil 571 } 572 573 func channelKeepaliveSender(ch Channel, in <-chan *Request, t *testing.T) { 574 defer ch.Close() 575 shell := newServerShell(ch, in, "> ") 576 readLine(shell, t) 577 if _, err := ch.SendRequest("keepalive@openssh.com", true, nil); err != nil { 578 t.Errorf("unable to send channel keepalive request: %v", err) 579 } 580 sendStatus(0, ch, t) 581 } 582 583 func TestClientWriteEOF(t *testing.T) { 584 conn := dial(simpleEchoHandler, t) 585 defer conn.Close() 586 587 session, err := conn.NewSession() 588 if err != nil { 589 t.Fatal(err) 590 } 591 defer session.Close() 592 stdin, err := session.StdinPipe() 593 if err != nil { 594 t.Fatalf("StdinPipe failed: %v", err) 595 } 596 stdout, err := session.StdoutPipe() 597 if err != nil { 598 t.Fatalf("StdoutPipe failed: %v", err) 599 } 600 601 data := []byte(`0000`) 602 _, err = stdin.Write(data) 603 if err != nil { 604 t.Fatalf("Write failed: %v", err) 605 } 606 stdin.Close() 607 608 res, err := ioutil.ReadAll(stdout) 609 if err != nil { 610 t.Fatalf("Read failed: %v", err) 611 } 612 613 if !bytes.Equal(data, res) { 614 t.Fatalf("Read differed from write, wrote: %v, read: %v", data, res) 615 } 616 } 617 618 func simpleEchoHandler(ch Channel, in <-chan *Request, t *testing.T) { 619 defer ch.Close() 620 data, err := ioutil.ReadAll(ch) 621 if err != nil { 622 t.Errorf("handler read error: %v", err) 623 } 624 _, err = ch.Write(data) 625 if err != nil { 626 t.Errorf("handler write error: %v", err) 627 } 628 } 629 630 func TestSessionID(t *testing.T) { 631 c1, c2, err := netPipe() 632 if err != nil { 633 t.Fatalf("netPipe: %v", err) 634 } 635 defer c1.Close() 636 defer c2.Close() 637 638 serverID := make(chan []byte, 1) 639 clientID := make(chan []byte, 1) 640 641 serverConf := &ServerConfig{ 642 NoClientAuth: true, 643 } 644 serverConf.AddHostKey(testSigners["ecdsa"]) 645 clientConf := &ClientConfig{ 646 User: "user", 647 } 648 649 go func() { 650 conn, chans, reqs, err := NewServerConn(c1, serverConf) 651 if err != nil { 652 t.Fatalf("server handshake: %v", err) 653 } 654 serverID <- conn.SessionID() 655 go DiscardRequests(reqs) 656 for ch := range chans { 657 ch.Reject(Prohibited, "") 658 } 659 }() 660 661 go func() { 662 conn, chans, reqs, err := NewClientConn(c2, "", clientConf) 663 if err != nil { 664 t.Fatalf("client handshake: %v", err) 665 } 666 clientID <- conn.SessionID() 667 go DiscardRequests(reqs) 668 for ch := range chans { 669 ch.Reject(Prohibited, "") 670 } 671 }() 672 673 s := <-serverID 674 c := <-clientID 675 if bytes.Compare(s, c) != 0 { 676 t.Errorf("server session ID (%x) != client session ID (%x)", s, c) 677 } else if len(s) == 0 { 678 t.Errorf("client and server SessionID were empty.") 679 } 680 }