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