session_test.go 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  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. "exp/terminal"
  9. "io"
  10. "testing"
  11. )
  12. type serverType func(*channel)
  13. // dial constructs a new test server and returns a *ClientConn.
  14. func dial(handler serverType, t *testing.T) *ClientConn {
  15. pw := password("tiger")
  16. serverConfig.PasswordCallback = func(user, pass string) bool {
  17. return user == "testuser" && pass == string(pw)
  18. }
  19. serverConfig.PublicKeyCallback = nil
  20. l, err := Listen("tcp", "127.0.0.1:0", serverConfig)
  21. if err != nil {
  22. t.Fatalf("unable to listen: %s", err)
  23. }
  24. go func() {
  25. defer l.Close()
  26. conn, err := l.Accept()
  27. if err != nil {
  28. t.Errorf("Unable to accept: %v", err)
  29. return
  30. }
  31. defer conn.Close()
  32. if err := conn.Handshake(); err != nil {
  33. t.Errorf("Unable to handshake: %v", err)
  34. return
  35. }
  36. for {
  37. ch, err := conn.Accept()
  38. if err == io.EOF {
  39. return
  40. }
  41. if err != nil {
  42. t.Errorf("Unable to accept incoming channel request: %v", err)
  43. return
  44. }
  45. if ch.ChannelType() != "session" {
  46. ch.Reject(UnknownChannelType, "unknown channel type")
  47. continue
  48. }
  49. ch.Accept()
  50. go handler(ch.(*channel))
  51. }
  52. t.Log("done")
  53. }()
  54. config := &ClientConfig{
  55. User: "testuser",
  56. Auth: []ClientAuth{
  57. ClientAuthPassword(pw),
  58. },
  59. }
  60. c, err := Dial("tcp", l.Addr().String(), config)
  61. if err != nil {
  62. t.Fatalf("unable to dial remote side: %s", err)
  63. }
  64. return c
  65. }
  66. // Test a simple string is returned to session.Stdout.
  67. func TestSessionShell(t *testing.T) {
  68. conn := dial(shellHandler, t)
  69. defer conn.Close()
  70. session, err := conn.NewSession()
  71. if err != nil {
  72. t.Fatalf("Unable to request new session: %s", err)
  73. }
  74. defer session.Close()
  75. stdout := new(bytes.Buffer)
  76. session.Stdout = stdout
  77. if err := session.Shell(); err != nil {
  78. t.Fatalf("Unable to execute command: %s", err)
  79. }
  80. if err := session.Wait(); err != nil {
  81. t.Fatalf("Remote command did not exit cleanly: %s", err)
  82. }
  83. actual := stdout.String()
  84. if actual != "golang" {
  85. t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual)
  86. }
  87. }
  88. // TODO(dfc) add support for Std{in,err}Pipe when the Server supports it.
  89. // Test a simple string is returned via StdoutPipe.
  90. func TestSessionStdoutPipe(t *testing.T) {
  91. conn := dial(shellHandler, t)
  92. defer conn.Close()
  93. session, err := conn.NewSession()
  94. if err != nil {
  95. t.Fatalf("Unable to request new session: %s", err)
  96. }
  97. defer session.Close()
  98. stdout, err := session.StdoutPipe()
  99. if err != nil {
  100. t.Fatalf("Unable to request StdoutPipe(): %v", err)
  101. }
  102. var buf bytes.Buffer
  103. if err := session.Shell(); err != nil {
  104. t.Fatalf("Unable to execute command: %s", err)
  105. }
  106. done := make(chan bool, 1)
  107. go func() {
  108. if _, err := io.Copy(&buf, stdout); err != nil {
  109. t.Errorf("Copy of stdout failed: %v", err)
  110. }
  111. done <- true
  112. }()
  113. if err := session.Wait(); err != nil {
  114. t.Fatalf("Remote command did not exit cleanly: %s", err)
  115. }
  116. <-done
  117. actual := buf.String()
  118. if actual != "golang" {
  119. t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual)
  120. }
  121. }
  122. // Test non-0 exit status is returned correctly.
  123. func TestExitStatusNonZero(t *testing.T) {
  124. conn := dial(exitStatusNonZeroHandler, t)
  125. defer conn.Close()
  126. session, err := conn.NewSession()
  127. if err != nil {
  128. t.Fatalf("Unable to request new session: %s", err)
  129. }
  130. defer session.Close()
  131. if err := session.Shell(); err != nil {
  132. t.Fatalf("Unable to execute command: %s", err)
  133. }
  134. err = session.Wait()
  135. if err == nil {
  136. t.Fatalf("expected command to fail but it didn't")
  137. }
  138. e, ok := err.(*ExitError)
  139. if !ok {
  140. t.Fatalf("expected *ExitError but got %T", err)
  141. }
  142. if e.ExitStatus() != 15 {
  143. t.Fatalf("expected command to exit with 15 but got %s", e.ExitStatus())
  144. }
  145. }
  146. // Test 0 exit status is returned correctly.
  147. func TestExitStatusZero(t *testing.T) {
  148. conn := dial(exitStatusZeroHandler, t)
  149. defer conn.Close()
  150. session, err := conn.NewSession()
  151. if err != nil {
  152. t.Fatalf("Unable to request new session: %s", err)
  153. }
  154. defer session.Close()
  155. if err := session.Shell(); err != nil {
  156. t.Fatalf("Unable to execute command: %s", err)
  157. }
  158. err = session.Wait()
  159. if err != nil {
  160. t.Fatalf("expected nil but got %s", err)
  161. }
  162. }
  163. // Test exit signal and status are both returned correctly.
  164. func TestExitSignalAndStatus(t *testing.T) {
  165. conn := dial(exitSignalAndStatusHandler, t)
  166. defer conn.Close()
  167. session, err := conn.NewSession()
  168. if err != nil {
  169. t.Fatalf("Unable to request new session: %s", err)
  170. }
  171. defer session.Close()
  172. if err := session.Shell(); err != nil {
  173. t.Fatalf("Unable to execute command: %s", err)
  174. }
  175. err = session.Wait()
  176. if err == nil {
  177. t.Fatalf("expected command to fail but it didn't")
  178. }
  179. e, ok := err.(*ExitError)
  180. if !ok {
  181. t.Fatalf("expected *ExitError but got %T", err)
  182. }
  183. if e.Signal() != "TERM" || e.ExitStatus() != 15 {
  184. t.Fatalf("expected command to exit with signal TERM and status 15 but got signal %s and status %v", e.Signal(), e.ExitStatus())
  185. }
  186. }
  187. // Test exit signal and status are both returned correctly.
  188. func TestKnownExitSignalOnly(t *testing.T) {
  189. conn := dial(exitSignalHandler, t)
  190. defer conn.Close()
  191. session, err := conn.NewSession()
  192. if err != nil {
  193. t.Fatalf("Unable to request new session: %s", err)
  194. }
  195. defer session.Close()
  196. if err := session.Shell(); err != nil {
  197. t.Fatalf("Unable to execute command: %s", err)
  198. }
  199. err = session.Wait()
  200. if err == nil {
  201. t.Fatalf("expected command to fail but it didn't")
  202. }
  203. e, ok := err.(*ExitError)
  204. if !ok {
  205. t.Fatalf("expected *ExitError but got %T", err)
  206. }
  207. if e.Signal() != "TERM" || e.ExitStatus() != 143 {
  208. t.Fatalf("expected command to exit with signal TERM and status 143 but got signal %s and status %v", e.Signal(), e.ExitStatus())
  209. }
  210. }
  211. // Test exit signal and status are both returned correctly.
  212. func TestUnknownExitSignal(t *testing.T) {
  213. conn := dial(exitSignalUnknownHandler, t)
  214. defer conn.Close()
  215. session, err := conn.NewSession()
  216. if err != nil {
  217. t.Fatalf("Unable to request new session: %s", err)
  218. }
  219. defer session.Close()
  220. if err := session.Shell(); err != nil {
  221. t.Fatalf("Unable to execute command: %s", err)
  222. }
  223. err = session.Wait()
  224. if err == nil {
  225. t.Fatalf("expected command to fail but it didn't")
  226. }
  227. e, ok := err.(*ExitError)
  228. if !ok {
  229. t.Fatalf("expected *ExitError but got %T", err)
  230. }
  231. if e.Signal() != "SYS" || e.ExitStatus() != 128 {
  232. t.Fatalf("expected command to exit with signal SYS and status 128 but got signal %s and status %v", e.Signal(), e.ExitStatus())
  233. }
  234. }
  235. // Test WaitMsg is not returned if the channel closes abruptly.
  236. func TestExitWithoutStatusOrSignal(t *testing.T) {
  237. conn := dial(exitWithoutSignalOrStatus, t)
  238. defer conn.Close()
  239. session, err := conn.NewSession()
  240. if err != nil {
  241. t.Fatalf("Unable to request new session: %s", err)
  242. }
  243. defer session.Close()
  244. if err := session.Shell(); err != nil {
  245. t.Fatalf("Unable to execute command: %s", err)
  246. }
  247. err = session.Wait()
  248. if err == nil {
  249. t.Fatalf("expected command to fail but it didn't")
  250. }
  251. _, ok := err.(*ExitError)
  252. if ok {
  253. // you can't actually test for errors.errorString
  254. // because it's not exported.
  255. t.Fatalf("expected *errorString but got %T", err)
  256. }
  257. }
  258. type exitStatusMsg struct {
  259. PeersId uint32
  260. Request string
  261. WantReply bool
  262. Status uint32
  263. }
  264. type exitSignalMsg struct {
  265. PeersId uint32
  266. Request string
  267. WantReply bool
  268. Signal string
  269. CoreDumped bool
  270. Errmsg string
  271. Lang string
  272. }
  273. func newServerShell(ch *channel, prompt string) *ServerTerminal {
  274. term := terminal.NewTerminal(ch, prompt)
  275. return &ServerTerminal{
  276. Term: term,
  277. Channel: ch,
  278. }
  279. }
  280. func exitStatusZeroHandler(ch *channel) {
  281. defer ch.Close()
  282. // this string is returned to stdout
  283. shell := newServerShell(ch, "> ")
  284. shell.ReadLine()
  285. sendStatus(0, ch)
  286. }
  287. func exitStatusNonZeroHandler(ch *channel) {
  288. defer ch.Close()
  289. shell := newServerShell(ch, "> ")
  290. shell.ReadLine()
  291. sendStatus(15, ch)
  292. }
  293. func exitSignalAndStatusHandler(ch *channel) {
  294. defer ch.Close()
  295. shell := newServerShell(ch, "> ")
  296. shell.ReadLine()
  297. sendStatus(15, ch)
  298. sendSignal("TERM", ch)
  299. }
  300. func exitSignalHandler(ch *channel) {
  301. defer ch.Close()
  302. shell := newServerShell(ch, "> ")
  303. shell.ReadLine()
  304. sendSignal("TERM", ch)
  305. }
  306. func exitSignalUnknownHandler(ch *channel) {
  307. defer ch.Close()
  308. shell := newServerShell(ch, "> ")
  309. shell.ReadLine()
  310. sendSignal("SYS", ch)
  311. }
  312. func exitWithoutSignalOrStatus(ch *channel) {
  313. defer ch.Close()
  314. shell := newServerShell(ch, "> ")
  315. shell.ReadLine()
  316. }
  317. func shellHandler(ch *channel) {
  318. defer ch.Close()
  319. // this string is returned to stdout
  320. shell := newServerShell(ch, "golang")
  321. shell.ReadLine()
  322. sendStatus(0, ch)
  323. }
  324. func sendStatus(status uint32, ch *channel) {
  325. msg := exitStatusMsg{
  326. PeersId: ch.theirId,
  327. Request: "exit-status",
  328. WantReply: false,
  329. Status: status,
  330. }
  331. ch.serverConn.writePacket(marshal(msgChannelRequest, msg))
  332. }
  333. func sendSignal(signal string, ch *channel) {
  334. sig := exitSignalMsg{
  335. PeersId: ch.theirId,
  336. Request: "exit-signal",
  337. WantReply: false,
  338. Signal: signal,
  339. CoreDumped: false,
  340. Errmsg: "Process terminated",
  341. Lang: "en-GB-oed",
  342. }
  343. ch.serverConn.writePacket(marshal(msgChannelRequest, sig))
  344. }