session_test.go 16 KB

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