blob: f35a378f2e3143cb60ffcac0eec07aa8d58f091a [file] [log] [blame]
Russ Cox470549d2012-01-25 15:31:12 -05001// 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
5package ssh
6
7// Session tests.
8
9import (
10 "bytes"
Dave Cheney06790d32012-08-28 08:06:15 +100011 crypto_rand "crypto/rand"
Han-Wen Nienhuys74f810a2015-05-08 18:14:00 +020012 "errors"
Russ Cox470549d2012-01-25 15:31:12 -050013 "io"
Dave Cheneyf4749cb2012-08-09 10:22:00 +100014 "io/ioutil"
Dave Cheney06790d32012-08-28 08:06:15 +100015 "math/rand"
Han-Wen Nienhuys74f810a2015-05-08 18:14:00 +020016 "net"
Russ Cox470549d2012-01-25 15:31:12 -050017 "testing"
David Symonds521edf82012-03-30 15:27:01 +110018
Andrew Gerranda73c6bb2014-11-10 08:50:25 +110019 "golang.org/x/crypto/ssh/terminal"
Russ Cox470549d2012-01-25 15:31:12 -050020)
21
Adam Langleyfa50e742014-04-09 13:57:52 -070022type serverType func(Channel, <-chan *Request, *testing.T)
Russ Cox470549d2012-01-25 15:31:12 -050023
24// dial constructs a new test server and returns a *ClientConn.
Adam Langleyfa50e742014-04-09 13:57:52 -070025func dial(handler serverType, t *testing.T) *Client {
26 c1, c2, err := netPipe()
Russ Cox470549d2012-01-25 15:31:12 -050027 if err != nil {
Adam Langleyfa50e742014-04-09 13:57:52 -070028 t.Fatalf("netPipe: %v", err)
Russ Cox470549d2012-01-25 15:31:12 -050029 }
Adam Langleyfa50e742014-04-09 13:57:52 -070030
Russ Cox470549d2012-01-25 15:31:12 -050031 go func() {
Adam Langleyfa50e742014-04-09 13:57:52 -070032 defer c1.Close()
33 conf := ServerConfig{
34 NoClientAuth: true,
35 }
36 conf.AddHostKey(testSigners["rsa"])
37
38 _, chans, reqs, err := NewServerConn(c1, &conf)
Russ Cox470549d2012-01-25 15:31:12 -050039 if err != nil {
Adam Langleyfa50e742014-04-09 13:57:52 -070040 t.Fatalf("Unable to handshake: %v", err)
Russ Cox470549d2012-01-25 15:31:12 -050041 }
Adam Langleyfa50e742014-04-09 13:57:52 -070042 go DiscardRequests(reqs)
43
44 for newCh := range chans {
45 if newCh.ChannelType() != "session" {
46 newCh.Reject(UnknownChannelType, "unknown channel type")
Russ Cox470549d2012-01-25 15:31:12 -050047 continue
48 }
Adam Langleyfa50e742014-04-09 13:57:52 -070049
50 ch, inReqs, err := newCh.Accept()
51 if err != nil {
52 t.Errorf("Accept: %v", err)
53 continue
54 }
Dave Cheney06790d32012-08-28 08:06:15 +100055 go func() {
Adam Langleyfa50e742014-04-09 13:57:52 -070056 handler(ch, inReqs, t)
Dave Cheney06790d32012-08-28 08:06:15 +100057 }()
Russ Cox470549d2012-01-25 15:31:12 -050058 }
Russ Cox470549d2012-01-25 15:31:12 -050059 }()
60
61 config := &ClientConfig{
62 User: "testuser",
Russ Cox470549d2012-01-25 15:31:12 -050063 }
64
Adam Langleyfa50e742014-04-09 13:57:52 -070065 conn, chans, reqs, err := NewClientConn(c2, "", config)
Russ Cox470549d2012-01-25 15:31:12 -050066 if err != nil {
Dave Cheney7343d5f2012-08-13 08:22:53 +100067 t.Fatalf("unable to dial remote side: %v", err)
Russ Cox470549d2012-01-25 15:31:12 -050068 }
Adam Langleyfa50e742014-04-09 13:57:52 -070069
70 return NewClient(conn, chans, reqs)
Russ Cox470549d2012-01-25 15:31:12 -050071}
72
73// Test a simple string is returned to session.Stdout.
74func TestSessionShell(t *testing.T) {
75 conn := dial(shellHandler, t)
76 defer conn.Close()
77 session, err := conn.NewSession()
78 if err != nil {
Dave Cheney7343d5f2012-08-13 08:22:53 +100079 t.Fatalf("Unable to request new session: %v", err)
Russ Cox470549d2012-01-25 15:31:12 -050080 }
81 defer session.Close()
82 stdout := new(bytes.Buffer)
83 session.Stdout = stdout
84 if err := session.Shell(); err != nil {
85 t.Fatalf("Unable to execute command: %s", err)
86 }
87 if err := session.Wait(); err != nil {
Dave Cheney7343d5f2012-08-13 08:22:53 +100088 t.Fatalf("Remote command did not exit cleanly: %v", err)
Russ Cox470549d2012-01-25 15:31:12 -050089 }
90 actual := stdout.String()
91 if actual != "golang" {
92 t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual)
93 }
94}
95
96// TODO(dfc) add support for Std{in,err}Pipe when the Server supports it.
97
98// Test a simple string is returned via StdoutPipe.
99func TestSessionStdoutPipe(t *testing.T) {
100 conn := dial(shellHandler, t)
101 defer conn.Close()
102 session, err := conn.NewSession()
103 if err != nil {
Dave Cheney7343d5f2012-08-13 08:22:53 +1000104 t.Fatalf("Unable to request new session: %v", err)
Russ Cox470549d2012-01-25 15:31:12 -0500105 }
106 defer session.Close()
107 stdout, err := session.StdoutPipe()
108 if err != nil {
109 t.Fatalf("Unable to request StdoutPipe(): %v", err)
110 }
111 var buf bytes.Buffer
112 if err := session.Shell(); err != nil {
Dave Cheney7343d5f2012-08-13 08:22:53 +1000113 t.Fatalf("Unable to execute command: %v", err)
Russ Cox470549d2012-01-25 15:31:12 -0500114 }
115 done := make(chan bool, 1)
116 go func() {
117 if _, err := io.Copy(&buf, stdout); err != nil {
118 t.Errorf("Copy of stdout failed: %v", err)
119 }
120 done <- true
121 }()
122 if err := session.Wait(); err != nil {
Dave Cheney7343d5f2012-08-13 08:22:53 +1000123 t.Fatalf("Remote command did not exit cleanly: %v", err)
Russ Cox470549d2012-01-25 15:31:12 -0500124 }
125 <-done
126 actual := buf.String()
127 if actual != "golang" {
128 t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual)
129 }
130}
131
Keith Rarick0e971cd2013-05-29 16:06:18 +1000132// Test that a simple string is returned via the Output helper,
133// and that stderr is discarded.
134func TestSessionOutput(t *testing.T) {
135 conn := dial(fixedOutputHandler, t)
136 defer conn.Close()
137 session, err := conn.NewSession()
138 if err != nil {
139 t.Fatalf("Unable to request new session: %v", err)
140 }
141 defer session.Close()
142
143 buf, err := session.Output("") // cmd is ignored by fixedOutputHandler
144 if err != nil {
145 t.Error("Remote command did not exit cleanly:", err)
146 }
147 w := "this-is-stdout."
148 g := string(buf)
149 if g != w {
150 t.Error("Remote command did not return expected string:")
151 t.Logf("want %q", w)
152 t.Logf("got %q", g)
153 }
154}
155
156// Test that both stdout and stderr are returned
157// via the CombinedOutput helper.
158func TestSessionCombinedOutput(t *testing.T) {
159 conn := dial(fixedOutputHandler, t)
160 defer conn.Close()
161 session, err := conn.NewSession()
162 if err != nil {
163 t.Fatalf("Unable to request new session: %v", err)
164 }
165 defer session.Close()
166
167 buf, err := session.CombinedOutput("") // cmd is ignored by fixedOutputHandler
168 if err != nil {
169 t.Error("Remote command did not exit cleanly:", err)
170 }
Dave Cheney1a6f1e62013-06-01 10:58:02 +1000171 const stdout = "this-is-stdout."
172 const stderr = "this-is-stderr."
Keith Rarick0e971cd2013-05-29 16:06:18 +1000173 g := string(buf)
Dave Cheney1a6f1e62013-06-01 10:58:02 +1000174 if g != stdout+stderr && g != stderr+stdout {
Keith Rarick0e971cd2013-05-29 16:06:18 +1000175 t.Error("Remote command did not return expected string:")
Dave Cheney1a6f1e62013-06-01 10:58:02 +1000176 t.Logf("want %q, or %q", stdout+stderr, stderr+stdout)
Keith Rarick0e971cd2013-05-29 16:06:18 +1000177 t.Logf("got %q", g)
178 }
179}
180
Russ Cox470549d2012-01-25 15:31:12 -0500181// Test non-0 exit status is returned correctly.
182func TestExitStatusNonZero(t *testing.T) {
183 conn := dial(exitStatusNonZeroHandler, t)
184 defer conn.Close()
185 session, err := conn.NewSession()
186 if err != nil {
Dave Cheney7343d5f2012-08-13 08:22:53 +1000187 t.Fatalf("Unable to request new session: %v", err)
Russ Cox470549d2012-01-25 15:31:12 -0500188 }
189 defer session.Close()
190 if err := session.Shell(); err != nil {
Dave Cheney7343d5f2012-08-13 08:22:53 +1000191 t.Fatalf("Unable to execute command: %v", err)
Russ Cox470549d2012-01-25 15:31:12 -0500192 }
193 err = session.Wait()
194 if err == nil {
195 t.Fatalf("expected command to fail but it didn't")
196 }
197 e, ok := err.(*ExitError)
198 if !ok {
199 t.Fatalf("expected *ExitError but got %T", err)
200 }
201 if e.ExitStatus() != 15 {
Dave Cheney7343d5f2012-08-13 08:22:53 +1000202 t.Fatalf("expected command to exit with 15 but got %v", e.ExitStatus())
Russ Cox470549d2012-01-25 15:31:12 -0500203 }
204}
205
206// Test 0 exit status is returned correctly.
207func TestExitStatusZero(t *testing.T) {
208 conn := dial(exitStatusZeroHandler, t)
209 defer conn.Close()
210 session, err := conn.NewSession()
211 if err != nil {
Dave Cheney7343d5f2012-08-13 08:22:53 +1000212 t.Fatalf("Unable to request new session: %v", err)
Russ Cox470549d2012-01-25 15:31:12 -0500213 }
214 defer session.Close()
215
216 if err := session.Shell(); err != nil {
Dave Cheney7343d5f2012-08-13 08:22:53 +1000217 t.Fatalf("Unable to execute command: %v", err)
Russ Cox470549d2012-01-25 15:31:12 -0500218 }
219 err = session.Wait()
220 if err != nil {
Dave Cheney7343d5f2012-08-13 08:22:53 +1000221 t.Fatalf("expected nil but got %v", err)
Russ Cox470549d2012-01-25 15:31:12 -0500222 }
223}
224
225// Test exit signal and status are both returned correctly.
226func TestExitSignalAndStatus(t *testing.T) {
227 conn := dial(exitSignalAndStatusHandler, t)
228 defer conn.Close()
229 session, err := conn.NewSession()
230 if err != nil {
Dave Cheney7343d5f2012-08-13 08:22:53 +1000231 t.Fatalf("Unable to request new session: %v", err)
Russ Cox470549d2012-01-25 15:31:12 -0500232 }
233 defer session.Close()
234 if err := session.Shell(); err != nil {
Dave Cheney7343d5f2012-08-13 08:22:53 +1000235 t.Fatalf("Unable to execute command: %v", err)
Russ Cox470549d2012-01-25 15:31:12 -0500236 }
237 err = session.Wait()
238 if err == nil {
239 t.Fatalf("expected command to fail but it didn't")
240 }
241 e, ok := err.(*ExitError)
242 if !ok {
243 t.Fatalf("expected *ExitError but got %T", err)
244 }
245 if e.Signal() != "TERM" || e.ExitStatus() != 15 {
246 t.Fatalf("expected command to exit with signal TERM and status 15 but got signal %s and status %v", e.Signal(), e.ExitStatus())
247 }
248}
249
250// Test exit signal and status are both returned correctly.
251func TestKnownExitSignalOnly(t *testing.T) {
252 conn := dial(exitSignalHandler, t)
253 defer conn.Close()
254 session, err := conn.NewSession()
255 if err != nil {
Dave Cheney7343d5f2012-08-13 08:22:53 +1000256 t.Fatalf("Unable to request new session: %v", err)
Russ Cox470549d2012-01-25 15:31:12 -0500257 }
258 defer session.Close()
259 if err := session.Shell(); err != nil {
Dave Cheney7343d5f2012-08-13 08:22:53 +1000260 t.Fatalf("Unable to execute command: %v", err)
Russ Cox470549d2012-01-25 15:31:12 -0500261 }
262 err = session.Wait()
263 if err == nil {
264 t.Fatalf("expected command to fail but it didn't")
265 }
266 e, ok := err.(*ExitError)
267 if !ok {
268 t.Fatalf("expected *ExitError but got %T", err)
269 }
270 if e.Signal() != "TERM" || e.ExitStatus() != 143 {
271 t.Fatalf("expected command to exit with signal TERM and status 143 but got signal %s and status %v", e.Signal(), e.ExitStatus())
272 }
273}
274
275// Test exit signal and status are both returned correctly.
276func TestUnknownExitSignal(t *testing.T) {
277 conn := dial(exitSignalUnknownHandler, t)
278 defer conn.Close()
279 session, err := conn.NewSession()
280 if err != nil {
Dave Cheney7343d5f2012-08-13 08:22:53 +1000281 t.Fatalf("Unable to request new session: %v", err)
Russ Cox470549d2012-01-25 15:31:12 -0500282 }
283 defer session.Close()
284 if err := session.Shell(); err != nil {
Dave Cheney7343d5f2012-08-13 08:22:53 +1000285 t.Fatalf("Unable to execute command: %v", err)
Russ Cox470549d2012-01-25 15:31:12 -0500286 }
287 err = session.Wait()
288 if err == nil {
289 t.Fatalf("expected command to fail but it didn't")
290 }
291 e, ok := err.(*ExitError)
292 if !ok {
293 t.Fatalf("expected *ExitError but got %T", err)
294 }
295 if e.Signal() != "SYS" || e.ExitStatus() != 128 {
296 t.Fatalf("expected command to exit with signal SYS and status 128 but got signal %s and status %v", e.Signal(), e.ExitStatus())
297 }
298}
299
Russ Cox470549d2012-01-25 15:31:12 -0500300func TestExitWithoutStatusOrSignal(t *testing.T) {
301 conn := dial(exitWithoutSignalOrStatus, t)
302 defer conn.Close()
303 session, err := conn.NewSession()
304 if err != nil {
Dave Cheney7343d5f2012-08-13 08:22:53 +1000305 t.Fatalf("Unable to request new session: %v", err)
Russ Cox470549d2012-01-25 15:31:12 -0500306 }
307 defer session.Close()
308 if err := session.Shell(); err != nil {
Dave Cheney7343d5f2012-08-13 08:22:53 +1000309 t.Fatalf("Unable to execute command: %v", err)
Russ Cox470549d2012-01-25 15:31:12 -0500310 }
311 err = session.Wait()
312 if err == nil {
313 t.Fatalf("expected command to fail but it didn't")
314 }
Han-Wen Nienhuysd81fdb72016-07-05 18:05:20 +0200315 if _, ok := err.(*ExitMissingError); !ok {
316 t.Fatalf("got %T want *ExitMissingError", err)
Russ Cox470549d2012-01-25 15:31:12 -0500317 }
318}
319
Dave Cheney06790d32012-08-28 08:06:15 +1000320// windowTestBytes is the number of bytes that we'll send to the SSH server.
321const windowTestBytes = 16000 * 200
322
323// TestServerWindow writes random data to the server. The server is expected to echo
324// the same data back, which is compared against the original.
325func TestServerWindow(t *testing.T) {
326 origBuf := bytes.NewBuffer(make([]byte, 0, windowTestBytes))
327 io.CopyN(origBuf, crypto_rand.Reader, windowTestBytes)
328 origBytes := origBuf.Bytes()
329
330 conn := dial(echoHandler, t)
331 defer conn.Close()
332 session, err := conn.NewSession()
333 if err != nil {
334 t.Fatal(err)
335 }
336 defer session.Close()
337 result := make(chan []byte)
338
339 go func() {
340 defer close(result)
341 echoedBuf := bytes.NewBuffer(make([]byte, 0, windowTestBytes))
342 serverStdout, err := session.StdoutPipe()
343 if err != nil {
344 t.Errorf("StdoutPipe failed: %v", err)
345 return
346 }
347 n, err := copyNRandomly("stdout", echoedBuf, serverStdout, windowTestBytes)
348 if err != nil && err != io.EOF {
349 t.Errorf("Read only %d bytes from server, expected %d: %v", n, windowTestBytes, err)
350 }
351 result <- echoedBuf.Bytes()
352 }()
353
354 serverStdin, err := session.StdinPipe()
355 if err != nil {
356 t.Fatalf("StdinPipe failed: %v", err)
357 }
358 written, err := copyNRandomly("stdin", serverStdin, origBuf, windowTestBytes)
359 if err != nil {
Jonathan Pittman44256fa2013-10-10 11:35:15 -0400360 t.Fatalf("failed to copy origBuf to serverStdin: %v", err)
Dave Cheney06790d32012-08-28 08:06:15 +1000361 }
362 if written != windowTestBytes {
363 t.Fatalf("Wrote only %d of %d bytes to server", written, windowTestBytes)
364 }
365
366 echoedBytes := <-result
367
368 if !bytes.Equal(origBytes, echoedBytes) {
369 t.Fatalf("Echoed buffer differed from original, orig %d, echoed %d", len(origBytes), len(echoedBytes))
370 }
371}
372
Eric Milliken61ab4d32012-12-18 11:33:58 -0500373// Verify the client can handle a keepalive packet from the server.
374func TestClientHandlesKeepalives(t *testing.T) {
375 conn := dial(channelKeepaliveSender, t)
376 defer conn.Close()
377 session, err := conn.NewSession()
378 if err != nil {
379 t.Fatal(err)
380 }
381 defer session.Close()
382 if err := session.Shell(); err != nil {
383 t.Fatalf("Unable to execute command: %v", err)
384 }
385 err = session.Wait()
386 if err != nil {
387 t.Fatalf("expected nil but got: %v", err)
388 }
389}
390
Russ Cox470549d2012-01-25 15:31:12 -0500391type exitStatusMsg struct {
Adam Langleyfa50e742014-04-09 13:57:52 -0700392 Status uint32
Russ Cox470549d2012-01-25 15:31:12 -0500393}
394
395type exitSignalMsg struct {
Russ Cox470549d2012-01-25 15:31:12 -0500396 Signal string
397 CoreDumped bool
398 Errmsg string
399 Lang string
400}
401
Adam Langleyfa50e742014-04-09 13:57:52 -0700402func handleTerminalRequests(in <-chan *Request) {
403 for req := range in {
404 ok := false
405 switch req.Type {
406 case "shell":
407 ok = true
408 if len(req.Payload) > 0 {
409 // We don't accept any commands, only the default shell.
410 ok = false
411 }
412 case "env":
413 ok = true
414 }
415 req.Reply(ok, nil)
Russ Cox470549d2012-01-25 15:31:12 -0500416 }
417}
418
Adam Langleyfa50e742014-04-09 13:57:52 -0700419func newServerShell(ch Channel, in <-chan *Request, prompt string) *terminal.Terminal {
420 term := terminal.NewTerminal(ch, prompt)
421 go handleTerminalRequests(in)
422 return term
423}
424
425func exitStatusZeroHandler(ch Channel, in <-chan *Request, t *testing.T) {
Russ Cox470549d2012-01-25 15:31:12 -0500426 defer ch.Close()
427 // this string is returned to stdout
Adam Langleyfa50e742014-04-09 13:57:52 -0700428 shell := newServerShell(ch, in, "> ")
Dave Cheneyd1710932012-08-25 17:52:59 +1000429 readLine(shell, t)
Dave Cheney7343d5f2012-08-13 08:22:53 +1000430 sendStatus(0, ch, t)
Russ Cox470549d2012-01-25 15:31:12 -0500431}
432
Adam Langleyfa50e742014-04-09 13:57:52 -0700433func exitStatusNonZeroHandler(ch Channel, in <-chan *Request, t *testing.T) {
Russ Cox470549d2012-01-25 15:31:12 -0500434 defer ch.Close()
Adam Langleyfa50e742014-04-09 13:57:52 -0700435 shell := newServerShell(ch, in, "> ")
Dave Cheneyd1710932012-08-25 17:52:59 +1000436 readLine(shell, t)
Dave Cheney7343d5f2012-08-13 08:22:53 +1000437 sendStatus(15, ch, t)
Russ Cox470549d2012-01-25 15:31:12 -0500438}
439
Adam Langleyfa50e742014-04-09 13:57:52 -0700440func exitSignalAndStatusHandler(ch Channel, in <-chan *Request, t *testing.T) {
Russ Cox470549d2012-01-25 15:31:12 -0500441 defer ch.Close()
Adam Langleyfa50e742014-04-09 13:57:52 -0700442 shell := newServerShell(ch, in, "> ")
Dave Cheneyd1710932012-08-25 17:52:59 +1000443 readLine(shell, t)
Dave Cheney7343d5f2012-08-13 08:22:53 +1000444 sendStatus(15, ch, t)
445 sendSignal("TERM", ch, t)
Russ Cox470549d2012-01-25 15:31:12 -0500446}
447
Adam Langleyfa50e742014-04-09 13:57:52 -0700448func exitSignalHandler(ch Channel, in <-chan *Request, t *testing.T) {
Russ Cox470549d2012-01-25 15:31:12 -0500449 defer ch.Close()
Adam Langleyfa50e742014-04-09 13:57:52 -0700450 shell := newServerShell(ch, in, "> ")
Dave Cheneyd1710932012-08-25 17:52:59 +1000451 readLine(shell, t)
Dave Cheney7343d5f2012-08-13 08:22:53 +1000452 sendSignal("TERM", ch, t)
Russ Cox470549d2012-01-25 15:31:12 -0500453}
454
Adam Langleyfa50e742014-04-09 13:57:52 -0700455func exitSignalUnknownHandler(ch Channel, in <-chan *Request, t *testing.T) {
Russ Cox470549d2012-01-25 15:31:12 -0500456 defer ch.Close()
Adam Langleyfa50e742014-04-09 13:57:52 -0700457 shell := newServerShell(ch, in, "> ")
Dave Cheneyd1710932012-08-25 17:52:59 +1000458 readLine(shell, t)
Dave Cheney7343d5f2012-08-13 08:22:53 +1000459 sendSignal("SYS", ch, t)
Russ Cox470549d2012-01-25 15:31:12 -0500460}
461
Adam Langleyfa50e742014-04-09 13:57:52 -0700462func exitWithoutSignalOrStatus(ch Channel, in <-chan *Request, t *testing.T) {
Russ Cox470549d2012-01-25 15:31:12 -0500463 defer ch.Close()
Adam Langleyfa50e742014-04-09 13:57:52 -0700464 shell := newServerShell(ch, in, "> ")
Dave Cheneyd1710932012-08-25 17:52:59 +1000465 readLine(shell, t)
Russ Cox470549d2012-01-25 15:31:12 -0500466}
467
Adam Langleyfa50e742014-04-09 13:57:52 -0700468func shellHandler(ch Channel, in <-chan *Request, t *testing.T) {
Russ Cox470549d2012-01-25 15:31:12 -0500469 defer ch.Close()
470 // this string is returned to stdout
Adam Langleyfa50e742014-04-09 13:57:52 -0700471 shell := newServerShell(ch, in, "golang")
Dave Cheneyd1710932012-08-25 17:52:59 +1000472 readLine(shell, t)
Dave Cheney7343d5f2012-08-13 08:22:53 +1000473 sendStatus(0, ch, t)
Russ Cox470549d2012-01-25 15:31:12 -0500474}
475
Keith Rarick0e971cd2013-05-29 16:06:18 +1000476// Ignores the command, writes fixed strings to stderr and stdout.
477// Strings are "this-is-stdout." and "this-is-stderr.".
Adam Langleyfa50e742014-04-09 13:57:52 -0700478func fixedOutputHandler(ch Channel, in <-chan *Request, t *testing.T) {
Keith Rarick0e971cd2013-05-29 16:06:18 +1000479 defer ch.Close()
Adam Langleyfa50e742014-04-09 13:57:52 -0700480 _, err := ch.Read(nil)
Keith Rarick0e971cd2013-05-29 16:06:18 +1000481
Adam Langleyfa50e742014-04-09 13:57:52 -0700482 req, ok := <-in
483 if !ok {
Keith Rarick0e971cd2013-05-29 16:06:18 +1000484 t.Fatalf("error: expected channel request, got: %#v", err)
485 return
486 }
Adam Langleyfa50e742014-04-09 13:57:52 -0700487
Keith Rarick0e971cd2013-05-29 16:06:18 +1000488 // ignore request, always send some text
Adam Langleyfa50e742014-04-09 13:57:52 -0700489 req.Reply(true, nil)
Keith Rarick0e971cd2013-05-29 16:06:18 +1000490
491 _, err = io.WriteString(ch, "this-is-stdout.")
492 if err != nil {
493 t.Fatalf("error writing on server: %v", err)
494 }
495 _, err = io.WriteString(ch.Stderr(), "this-is-stderr.")
496 if err != nil {
497 t.Fatalf("error writing on server: %v", err)
498 }
499 sendStatus(0, ch, t)
500}
501
Adam Langleyfa50e742014-04-09 13:57:52 -0700502func readLine(shell *terminal.Terminal, t *testing.T) {
Dave Cheneyd1710932012-08-25 17:52:59 +1000503 if _, err := shell.ReadLine(); err != nil && err != io.EOF {
Dave Cheney06790d32012-08-28 08:06:15 +1000504 t.Errorf("unable to read line: %v", err)
Dave Cheneyd1710932012-08-25 17:52:59 +1000505 }
506}
507
Adam Langleyfa50e742014-04-09 13:57:52 -0700508func sendStatus(status uint32, ch Channel, t *testing.T) {
Russ Cox470549d2012-01-25 15:31:12 -0500509 msg := exitStatusMsg{
Adam Langleyfa50e742014-04-09 13:57:52 -0700510 Status: status,
Russ Cox470549d2012-01-25 15:31:12 -0500511 }
Adam Langleyfa50e742014-04-09 13:57:52 -0700512 if _, err := ch.SendRequest("exit-status", false, Marshal(&msg)); err != nil {
Dave Cheney7343d5f2012-08-13 08:22:53 +1000513 t.Errorf("unable to send status: %v", err)
514 }
Russ Cox470549d2012-01-25 15:31:12 -0500515}
516
Adam Langleyfa50e742014-04-09 13:57:52 -0700517func sendSignal(signal string, ch Channel, t *testing.T) {
Russ Cox470549d2012-01-25 15:31:12 -0500518 sig := exitSignalMsg{
Russ Cox470549d2012-01-25 15:31:12 -0500519 Signal: signal,
520 CoreDumped: false,
521 Errmsg: "Process terminated",
522 Lang: "en-GB-oed",
523 }
Adam Langleyfa50e742014-04-09 13:57:52 -0700524 if _, err := ch.SendRequest("exit-signal", false, Marshal(&sig)); err != nil {
Dave Cheney7343d5f2012-08-13 08:22:53 +1000525 t.Errorf("unable to send signal: %v", err)
526 }
Russ Cox470549d2012-01-25 15:31:12 -0500527}
Adam Langleybcdd6a22012-04-24 13:46:22 -0400528
Adam Langleyfa50e742014-04-09 13:57:52 -0700529func discardHandler(ch Channel, t *testing.T) {
Adam Langleybcdd6a22012-04-24 13:46:22 -0400530 defer ch.Close()
Dave Cheney06790d32012-08-28 08:06:15 +1000531 io.Copy(ioutil.Discard, ch)
Dave Cheneyf4749cb2012-08-09 10:22:00 +1000532}
Dave Cheney7343d5f2012-08-13 08:22:53 +1000533
Adam Langleyfa50e742014-04-09 13:57:52 -0700534func echoHandler(ch Channel, in <-chan *Request, t *testing.T) {
Dave Cheney06790d32012-08-28 08:06:15 +1000535 defer ch.Close()
536 if n, err := copyNRandomly("echohandler", ch, ch, windowTestBytes); err != nil {
537 t.Errorf("short write, wrote %d, expected %d: %v ", n, windowTestBytes, err)
538 }
539}
540
541// copyNRandomly copies n bytes from src to dst. It uses a variable, and random,
542// buffer size to exercise more code paths.
543func copyNRandomly(title string, dst io.Writer, src io.Reader, n int) (int, error) {
544 var (
545 buf = make([]byte, 32*1024)
546 written int
547 remaining = n
548 )
549 for remaining > 0 {
550 l := rand.Intn(1 << 15)
551 if remaining < l {
552 l = remaining
553 }
554 nr, er := src.Read(buf[:l])
555 nw, ew := dst.Write(buf[:nr])
556 remaining -= nw
557 written += nw
558 if ew != nil {
559 return written, ew
560 }
561 if nr != nw {
562 return written, io.ErrShortWrite
563 }
564 if er != nil && er != io.EOF {
565 return written, er
566 }
567 }
568 return written, nil
569}
Eric Milliken61ab4d32012-12-18 11:33:58 -0500570
Adam Langleyfa50e742014-04-09 13:57:52 -0700571func channelKeepaliveSender(ch Channel, in <-chan *Request, t *testing.T) {
Eric Milliken61ab4d32012-12-18 11:33:58 -0500572 defer ch.Close()
Adam Langleyfa50e742014-04-09 13:57:52 -0700573 shell := newServerShell(ch, in, "> ")
Eric Milliken61ab4d32012-12-18 11:33:58 -0500574 readLine(shell, t)
Adam Langleyfa50e742014-04-09 13:57:52 -0700575 if _, err := ch.SendRequest("keepalive@openssh.com", true, nil); err != nil {
Eric Milliken61ab4d32012-12-18 11:33:58 -0500576 t.Errorf("unable to send channel keepalive request: %v", err)
577 }
578 sendStatus(0, ch, t)
579}
Adam Langleyfa50e742014-04-09 13:57:52 -0700580
581func TestClientWriteEOF(t *testing.T) {
582 conn := dial(simpleEchoHandler, t)
583 defer conn.Close()
584
585 session, err := conn.NewSession()
586 if err != nil {
587 t.Fatal(err)
588 }
589 defer session.Close()
590 stdin, err := session.StdinPipe()
591 if err != nil {
592 t.Fatalf("StdinPipe failed: %v", err)
593 }
594 stdout, err := session.StdoutPipe()
595 if err != nil {
596 t.Fatalf("StdoutPipe failed: %v", err)
597 }
598
599 data := []byte(`0000`)
600 _, err = stdin.Write(data)
601 if err != nil {
602 t.Fatalf("Write failed: %v", err)
603 }
604 stdin.Close()
605
606 res, err := ioutil.ReadAll(stdout)
607 if err != nil {
608 t.Fatalf("Read failed: %v", err)
609 }
610
611 if !bytes.Equal(data, res) {
612 t.Fatalf("Read differed from write, wrote: %v, read: %v", data, res)
613 }
614}
615
616func simpleEchoHandler(ch Channel, in <-chan *Request, t *testing.T) {
617 defer ch.Close()
618 data, err := ioutil.ReadAll(ch)
619 if err != nil {
620 t.Errorf("handler read error: %v", err)
621 }
622 _, err = ch.Write(data)
623 if err != nil {
624 t.Errorf("handler write error: %v", err)
625 }
626}
Han-Wen Nienhuys88b65fb2015-02-04 14:11:50 +0100627
628func TestSessionID(t *testing.T) {
629 c1, c2, err := netPipe()
630 if err != nil {
631 t.Fatalf("netPipe: %v", err)
632 }
633 defer c1.Close()
634 defer c2.Close()
635
636 serverID := make(chan []byte, 1)
637 clientID := make(chan []byte, 1)
638
639 serverConf := &ServerConfig{
640 NoClientAuth: true,
641 }
642 serverConf.AddHostKey(testSigners["ecdsa"])
643 clientConf := &ClientConfig{
644 User: "user",
645 }
646
647 go func() {
648 conn, chans, reqs, err := NewServerConn(c1, serverConf)
649 if err != nil {
650 t.Fatalf("server handshake: %v", err)
651 }
652 serverID <- conn.SessionID()
653 go DiscardRequests(reqs)
654 for ch := range chans {
655 ch.Reject(Prohibited, "")
656 }
657 }()
658
659 go func() {
660 conn, chans, reqs, err := NewClientConn(c2, "", clientConf)
661 if err != nil {
662 t.Fatalf("client handshake: %v", err)
663 }
664 clientID <- conn.SessionID()
665 go DiscardRequests(reqs)
666 for ch := range chans {
667 ch.Reject(Prohibited, "")
668 }
669 }()
670
671 s := <-serverID
672 c := <-clientID
673 if bytes.Compare(s, c) != 0 {
674 t.Errorf("server session ID (%x) != client session ID (%x)", s, c)
675 } else if len(s) == 0 {
676 t.Errorf("client and server SessionID were empty.")
677 }
678}
Han-Wen Nienhuys74f810a2015-05-08 18:14:00 +0200679
680type noReadConn struct {
681 readSeen bool
682 net.Conn
683}
684
685func (c *noReadConn) Close() error {
686 return nil
687}
688
689func (c *noReadConn) Read(b []byte) (int, error) {
690 c.readSeen = true
691 return 0, errors.New("noReadConn error")
692}
693
694func TestInvalidServerConfiguration(t *testing.T) {
695 c1, c2, err := netPipe()
696 if err != nil {
697 t.Fatalf("netPipe: %v", err)
698 }
699 defer c1.Close()
700 defer c2.Close()
701
702 serveConn := noReadConn{Conn: c1}
703 serverConf := &ServerConfig{}
704
705 NewServerConn(&serveConn, serverConf)
706 if serveConn.readSeen {
707 t.Fatalf("NewServerConn attempted to Read() from Conn while configuration is missing host key")
708 }
709
710 serverConf.AddHostKey(testSigners["ecdsa"])
711
712 NewServerConn(&serveConn, serverConf)
713 if serveConn.readSeen {
714 t.Fatalf("NewServerConn attempted to Read() from Conn while configuration is missing authentication method")
715 }
716}
hanwen2f3083f2015-07-31 22:59:06 +0200717
718func TestHostKeyAlgorithms(t *testing.T) {
719 serverConf := &ServerConfig{
720 NoClientAuth: true,
721 }
722 serverConf.AddHostKey(testSigners["rsa"])
723 serverConf.AddHostKey(testSigners["ecdsa"])
724
725 connect := func(clientConf *ClientConfig, want string) {
726 var alg string
727 clientConf.HostKeyCallback = func(h string, a net.Addr, key PublicKey) error {
728 alg = key.Type()
729 return nil
730 }
731 c1, c2, err := netPipe()
732 if err != nil {
733 t.Fatalf("netPipe: %v", err)
734 }
735 defer c1.Close()
736 defer c2.Close()
737
738 go NewServerConn(c1, serverConf)
739 _, _, _, err = NewClientConn(c2, "", clientConf)
740 if err != nil {
741 t.Fatalf("NewClientConn: %v", err)
742 }
743 if alg != want {
744 t.Errorf("selected key algorithm %s, want %s", alg, want)
745 }
746 }
747
748 // By default, we get the preferred algorithm, which is ECDSA 256.
749
750 clientConf := &ClientConfig{}
751 connect(clientConf, KeyAlgoECDSA256)
752
753 // Client asks for RSA explicitly.
754 clientConf.HostKeyAlgorithms = []string{KeyAlgoRSA}
755 connect(clientConf, KeyAlgoRSA)
756
757 c1, c2, err := netPipe()
758 if err != nil {
759 t.Fatalf("netPipe: %v", err)
760 }
761 defer c1.Close()
762 defer c2.Close()
763
764 go NewServerConn(c1, serverConf)
765 clientConf.HostKeyAlgorithms = []string{"nonexistent-hostkey-algo"}
766 _, _, _, err = NewClientConn(c2, "", clientConf)
767 if err == nil {
768 t.Fatal("succeeded connecting with unknown hostkey algorithm")
769 }
770}