github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/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  	"errors"
    13  	"io"
    14  	"io/ioutil"
    15  	"math/rand"
    16  	"net"
    17  	"testing"
    18  
    19  	"golang.org/x/crypto/ssh/terminal"
    20  )
    21  
    22  type serverType func(Channel, <-chan *Request, *testing.T)
    23  
    24  // dial constructs a new test server and returns a *ClientConn.
    25  func dial(handler serverType, t *testing.T) *Client {
    26  	c1, c2, err := netPipe()
    27  	if err != nil {
    28  		t.Fatalf("netPipe: %v", err)
    29  	}
    30  
    31  	go func() {
    32  		defer c1.Close()
    33  		conf := ServerConfig{
    34  			NoClientAuth: true,
    35  		}
    36  		conf.AddHostKey(testSigners["rsa"])
    37  
    38  		_, chans, reqs, err := NewServerConn(c1, &conf)
    39  		if err != nil {
    40  			t.Fatalf("Unable to handshake: %v", err)
    41  		}
    42  		go DiscardRequests(reqs)
    43  
    44  		for newCh := range chans {
    45  			if newCh.ChannelType() != "session" {
    46  				newCh.Reject(UnknownChannelType, "unknown channel type")
    47  				continue
    48  			}
    49  
    50  			ch, inReqs, err := newCh.Accept()
    51  			if err != nil {
    52  				t.Errorf("Accept: %v", err)
    53  				continue
    54  			}
    55  			go func() {
    56  				handler(ch, inReqs, t)
    57  			}()
    58  		}
    59  	}()
    60  
    61  	config := &ClientConfig{
    62  		User: "testuser",
    63  	}
    64  
    65  	conn, chans, reqs, err := NewClientConn(c2, "", config)
    66  	if err != nil {
    67  		t.Fatalf("unable to dial remote side: %v", err)
    68  	}
    69  
    70  	return NewClient(conn, chans, reqs)
    71  }
    72  
    73  // Test a simple string is returned to session.Stdout.
    74  func TestSessionShell(t *testing.T) {
    75  	conn := dial(shellHandler, t)
    76  	defer conn.Close()
    77  	session, err := conn.NewSession()
    78  	if err != nil {
    79  		t.Fatalf("Unable to request new session: %v", err)
    80  	}
    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 {
    88  		t.Fatalf("Remote command did not exit cleanly: %v", err)
    89  	}
    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.
    99  func TestSessionStdoutPipe(t *testing.T) {
   100  	conn := dial(shellHandler, t)
   101  	defer conn.Close()
   102  	session, err := conn.NewSession()
   103  	if err != nil {
   104  		t.Fatalf("Unable to request new session: %v", err)
   105  	}
   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 {
   113  		t.Fatalf("Unable to execute command: %v", err)
   114  	}
   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 {
   123  		t.Fatalf("Remote command did not exit cleanly: %v", err)
   124  	}
   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  
   132  // Test that a simple string is returned via the Output helper,
   133  // and that stderr is discarded.
   134  func 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.
   158  func 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  	}
   171  	const stdout = "this-is-stdout."
   172  	const stderr = "this-is-stderr."
   173  	g := string(buf)
   174  	if g != stdout+stderr && g != stderr+stdout {
   175  		t.Error("Remote command did not return expected string:")
   176  		t.Logf("want %q, or %q", stdout+stderr, stderr+stdout)
   177  		t.Logf("got  %q", g)
   178  	}
   179  }
   180  
   181  // Test non-0 exit status is returned correctly.
   182  func TestExitStatusNonZero(t *testing.T) {
   183  	conn := dial(exitStatusNonZeroHandler, t)
   184  	defer conn.Close()
   185  	session, err := conn.NewSession()
   186  	if err != nil {
   187  		t.Fatalf("Unable to request new session: %v", err)
   188  	}
   189  	defer session.Close()
   190  	if err := session.Shell(); err != nil {
   191  		t.Fatalf("Unable to execute command: %v", err)
   192  	}
   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 {
   202  		t.Fatalf("expected command to exit with 15 but got %v", e.ExitStatus())
   203  	}
   204  }
   205  
   206  // Test 0 exit status is returned correctly.
   207  func TestExitStatusZero(t *testing.T) {
   208  	conn := dial(exitStatusZeroHandler, t)
   209  	defer conn.Close()
   210  	session, err := conn.NewSession()
   211  	if err != nil {
   212  		t.Fatalf("Unable to request new session: %v", err)
   213  	}
   214  	defer session.Close()
   215  
   216  	if err := session.Shell(); err != nil {
   217  		t.Fatalf("Unable to execute command: %v", err)
   218  	}
   219  	err = session.Wait()
   220  	if err != nil {
   221  		t.Fatalf("expected nil but got %v", err)
   222  	}
   223  }
   224  
   225  // Test exit signal and status are both returned correctly.
   226  func TestExitSignalAndStatus(t *testing.T) {
   227  	conn := dial(exitSignalAndStatusHandler, t)
   228  	defer conn.Close()
   229  	session, err := conn.NewSession()
   230  	if err != nil {
   231  		t.Fatalf("Unable to request new session: %v", err)
   232  	}
   233  	defer session.Close()
   234  	if err := session.Shell(); err != nil {
   235  		t.Fatalf("Unable to execute command: %v", err)
   236  	}
   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.
   251  func TestKnownExitSignalOnly(t *testing.T) {
   252  	conn := dial(exitSignalHandler, t)
   253  	defer conn.Close()
   254  	session, err := conn.NewSession()
   255  	if err != nil {
   256  		t.Fatalf("Unable to request new session: %v", err)
   257  	}
   258  	defer session.Close()
   259  	if err := session.Shell(); err != nil {
   260  		t.Fatalf("Unable to execute command: %v", err)
   261  	}
   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.
   276  func TestUnknownExitSignal(t *testing.T) {
   277  	conn := dial(exitSignalUnknownHandler, t)
   278  	defer conn.Close()
   279  	session, err := conn.NewSession()
   280  	if err != nil {
   281  		t.Fatalf("Unable to request new session: %v", err)
   282  	}
   283  	defer session.Close()
   284  	if err := session.Shell(); err != nil {
   285  		t.Fatalf("Unable to execute command: %v", err)
   286  	}
   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  
   300  // Test WaitMsg is not returned if the channel closes abruptly.
   301  func TestExitWithoutStatusOrSignal(t *testing.T) {
   302  	conn := dial(exitWithoutSignalOrStatus, t)
   303  	defer conn.Close()
   304  	session, err := conn.NewSession()
   305  	if err != nil {
   306  		t.Fatalf("Unable to request new session: %v", err)
   307  	}
   308  	defer session.Close()
   309  	if err := session.Shell(); err != nil {
   310  		t.Fatalf("Unable to execute command: %v", err)
   311  	}
   312  	err = session.Wait()
   313  	if err == nil {
   314  		t.Fatalf("expected command to fail but it didn't")
   315  	}
   316  	_, ok := err.(*ExitError)
   317  	if ok {
   318  		// you can't actually test for errors.errorString
   319  		// because it's not exported.
   320  		t.Fatalf("expected *errorString but got %T", err)
   321  	}
   322  }
   323  
   324  // windowTestBytes is the number of bytes that we'll send to the SSH server.
   325  const windowTestBytes = 16000 * 200
   326  
   327  // TestServerWindow writes random data to the server. The server is expected to echo
   328  // the same data back, which is compared against the original.
   329  func TestServerWindow(t *testing.T) {
   330  	origBuf := bytes.NewBuffer(make([]byte, 0, windowTestBytes))
   331  	io.CopyN(origBuf, crypto_rand.Reader, windowTestBytes)
   332  	origBytes := origBuf.Bytes()
   333  
   334  	conn := dial(echoHandler, t)
   335  	defer conn.Close()
   336  	session, err := conn.NewSession()
   337  	if err != nil {
   338  		t.Fatal(err)
   339  	}
   340  	defer session.Close()
   341  	result := make(chan []byte)
   342  
   343  	go func() {
   344  		defer close(result)
   345  		echoedBuf := bytes.NewBuffer(make([]byte, 0, windowTestBytes))
   346  		serverStdout, err := session.StdoutPipe()
   347  		if err != nil {
   348  			t.Errorf("StdoutPipe failed: %v", err)
   349  			return
   350  		}
   351  		n, err := copyNRandomly("stdout", echoedBuf, serverStdout, windowTestBytes)
   352  		if err != nil && err != io.EOF {
   353  			t.Errorf("Read only %d bytes from server, expected %d: %v", n, windowTestBytes, err)
   354  		}
   355  		result <- echoedBuf.Bytes()
   356  	}()
   357  
   358  	serverStdin, err := session.StdinPipe()
   359  	if err != nil {
   360  		t.Fatalf("StdinPipe failed: %v", err)
   361  	}
   362  	written, err := copyNRandomly("stdin", serverStdin, origBuf, windowTestBytes)
   363  	if err != nil {
   364  		t.Fatalf("failed to copy origBuf to serverStdin: %v", err)
   365  	}
   366  	if written != windowTestBytes {
   367  		t.Fatalf("Wrote only %d of %d bytes to server", written, windowTestBytes)
   368  	}
   369  
   370  	echoedBytes := <-result
   371  
   372  	if !bytes.Equal(origBytes, echoedBytes) {
   373  		t.Fatalf("Echoed buffer differed from original, orig %d, echoed %d", len(origBytes), len(echoedBytes))
   374  	}
   375  }
   376  
   377  // Verify the client can handle a keepalive packet from the server.
   378  func TestClientHandlesKeepalives(t *testing.T) {
   379  	conn := dial(channelKeepaliveSender, t)
   380  	defer conn.Close()
   381  	session, err := conn.NewSession()
   382  	if err != nil {
   383  		t.Fatal(err)
   384  	}
   385  	defer session.Close()
   386  	if err := session.Shell(); err != nil {
   387  		t.Fatalf("Unable to execute command: %v", err)
   388  	}
   389  	err = session.Wait()
   390  	if err != nil {
   391  		t.Fatalf("expected nil but got: %v", err)
   392  	}
   393  }
   394  
   395  type exitStatusMsg struct {
   396  	Status uint32
   397  }
   398  
   399  type exitSignalMsg struct {
   400  	Signal     string
   401  	CoreDumped bool
   402  	Errmsg     string
   403  	Lang       string
   404  }
   405  
   406  func handleTerminalRequests(in <-chan *Request) {
   407  	for req := range in {
   408  		ok := false
   409  		switch req.Type {
   410  		case "shell":
   411  			ok = true
   412  			if len(req.Payload) > 0 {
   413  				// We don't accept any commands, only the default shell.
   414  				ok = false
   415  			}
   416  		case "env":
   417  			ok = true
   418  		}
   419  		req.Reply(ok, nil)
   420  	}
   421  }
   422  
   423  func newServerShell(ch Channel, in <-chan *Request, prompt string) *terminal.Terminal {
   424  	term := terminal.NewTerminal(ch, prompt)
   425  	go handleTerminalRequests(in)
   426  	return term
   427  }
   428  
   429  func exitStatusZeroHandler(ch Channel, in <-chan *Request, t *testing.T) {
   430  	defer ch.Close()
   431  	// this string is returned to stdout
   432  	shell := newServerShell(ch, in, "> ")
   433  	readLine(shell, t)
   434  	sendStatus(0, ch, t)
   435  }
   436  
   437  func exitStatusNonZeroHandler(ch Channel, in <-chan *Request, t *testing.T) {
   438  	defer ch.Close()
   439  	shell := newServerShell(ch, in, "> ")
   440  	readLine(shell, t)
   441  	sendStatus(15, ch, t)
   442  }
   443  
   444  func exitSignalAndStatusHandler(ch Channel, in <-chan *Request, t *testing.T) {
   445  	defer ch.Close()
   446  	shell := newServerShell(ch, in, "> ")
   447  	readLine(shell, t)
   448  	sendStatus(15, ch, t)
   449  	sendSignal("TERM", ch, t)
   450  }
   451  
   452  func exitSignalHandler(ch Channel, in <-chan *Request, t *testing.T) {
   453  	defer ch.Close()
   454  	shell := newServerShell(ch, in, "> ")
   455  	readLine(shell, t)
   456  	sendSignal("TERM", ch, t)
   457  }
   458  
   459  func exitSignalUnknownHandler(ch Channel, in <-chan *Request, t *testing.T) {
   460  	defer ch.Close()
   461  	shell := newServerShell(ch, in, "> ")
   462  	readLine(shell, t)
   463  	sendSignal("SYS", ch, t)
   464  }
   465  
   466  func exitWithoutSignalOrStatus(ch Channel, in <-chan *Request, t *testing.T) {
   467  	defer ch.Close()
   468  	shell := newServerShell(ch, in, "> ")
   469  	readLine(shell, t)
   470  }
   471  
   472  func shellHandler(ch Channel, in <-chan *Request, t *testing.T) {
   473  	defer ch.Close()
   474  	// this string is returned to stdout
   475  	shell := newServerShell(ch, in, "golang")
   476  	readLine(shell, t)
   477  	sendStatus(0, ch, t)
   478  }
   479  
   480  // Ignores the command, writes fixed strings to stderr and stdout.
   481  // Strings are "this-is-stdout." and "this-is-stderr.".
   482  func fixedOutputHandler(ch Channel, in <-chan *Request, t *testing.T) {
   483  	defer ch.Close()
   484  	_, err := ch.Read(nil)
   485  
   486  	req, ok := <-in
   487  	if !ok {
   488  		t.Fatalf("error: expected channel request, got: %#v", err)
   489  		return
   490  	}
   491  
   492  	// ignore request, always send some text
   493  	req.Reply(true, nil)
   494  
   495  	_, err = io.WriteString(ch, "this-is-stdout.")
   496  	if err != nil {
   497  		t.Fatalf("error writing on server: %v", err)
   498  	}
   499  	_, err = io.WriteString(ch.Stderr(), "this-is-stderr.")
   500  	if err != nil {
   501  		t.Fatalf("error writing on server: %v", err)
   502  	}
   503  	sendStatus(0, ch, t)
   504  }
   505  
   506  func readLine(shell *terminal.Terminal, t *testing.T) {
   507  	if _, err := shell.ReadLine(); err != nil && err != io.EOF {
   508  		t.Errorf("unable to read line: %v", err)
   509  	}
   510  }
   511  
   512  func sendStatus(status uint32, ch Channel, t *testing.T) {
   513  	msg := exitStatusMsg{
   514  		Status: status,
   515  	}
   516  	if _, err := ch.SendRequest("exit-status", false, Marshal(&msg)); err != nil {
   517  		t.Errorf("unable to send status: %v", err)
   518  	}
   519  }
   520  
   521  func sendSignal(signal string, ch Channel, t *testing.T) {
   522  	sig := exitSignalMsg{
   523  		Signal:     signal,
   524  		CoreDumped: false,
   525  		Errmsg:     "Process terminated",
   526  		Lang:       "en-GB-oed",
   527  	}
   528  	if _, err := ch.SendRequest("exit-signal", false, Marshal(&sig)); err != nil {
   529  		t.Errorf("unable to send signal: %v", err)
   530  	}
   531  }
   532  
   533  func discardHandler(ch Channel, t *testing.T) {
   534  	defer ch.Close()
   535  	io.Copy(ioutil.Discard, ch)
   536  }
   537  
   538  func echoHandler(ch Channel, in <-chan *Request, t *testing.T) {
   539  	defer ch.Close()
   540  	if n, err := copyNRandomly("echohandler", ch, ch, windowTestBytes); err != nil {
   541  		t.Errorf("short write, wrote %d, expected %d: %v ", n, windowTestBytes, err)
   542  	}
   543  }
   544  
   545  // copyNRandomly copies n bytes from src to dst. It uses a variable, and random,
   546  // buffer size to exercise more code paths.
   547  func copyNRandomly(title string, dst io.Writer, src io.Reader, n int) (int, error) {
   548  	var (
   549  		buf       = make([]byte, 32*1024)
   550  		written   int
   551  		remaining = n
   552  	)
   553  	for remaining > 0 {
   554  		l := rand.Intn(1 << 15)
   555  		if remaining < l {
   556  			l = remaining
   557  		}
   558  		nr, er := src.Read(buf[:l])
   559  		nw, ew := dst.Write(buf[:nr])
   560  		remaining -= nw
   561  		written += nw
   562  		if ew != nil {
   563  			return written, ew
   564  		}
   565  		if nr != nw {
   566  			return written, io.ErrShortWrite
   567  		}
   568  		if er != nil && er != io.EOF {
   569  			return written, er
   570  		}
   571  	}
   572  	return written, nil
   573  }
   574  
   575  func channelKeepaliveSender(ch Channel, in <-chan *Request, t *testing.T) {
   576  	defer ch.Close()
   577  	shell := newServerShell(ch, in, "> ")
   578  	readLine(shell, t)
   579  	if _, err := ch.SendRequest("keepalive@openssh.com", true, nil); err != nil {
   580  		t.Errorf("unable to send channel keepalive request: %v", err)
   581  	}
   582  	sendStatus(0, ch, t)
   583  }
   584  
   585  func TestClientWriteEOF(t *testing.T) {
   586  	conn := dial(simpleEchoHandler, t)
   587  	defer conn.Close()
   588  
   589  	session, err := conn.NewSession()
   590  	if err != nil {
   591  		t.Fatal(err)
   592  	}
   593  	defer session.Close()
   594  	stdin, err := session.StdinPipe()
   595  	if err != nil {
   596  		t.Fatalf("StdinPipe failed: %v", err)
   597  	}
   598  	stdout, err := session.StdoutPipe()
   599  	if err != nil {
   600  		t.Fatalf("StdoutPipe failed: %v", err)
   601  	}
   602  
   603  	data := []byte(`0000`)
   604  	_, err = stdin.Write(data)
   605  	if err != nil {
   606  		t.Fatalf("Write failed: %v", err)
   607  	}
   608  	stdin.Close()
   609  
   610  	res, err := ioutil.ReadAll(stdout)
   611  	if err != nil {
   612  		t.Fatalf("Read failed: %v", err)
   613  	}
   614  
   615  	if !bytes.Equal(data, res) {
   616  		t.Fatalf("Read differed from write, wrote: %v, read: %v", data, res)
   617  	}
   618  }
   619  
   620  func simpleEchoHandler(ch Channel, in <-chan *Request, t *testing.T) {
   621  	defer ch.Close()
   622  	data, err := ioutil.ReadAll(ch)
   623  	if err != nil {
   624  		t.Errorf("handler read error: %v", err)
   625  	}
   626  	_, err = ch.Write(data)
   627  	if err != nil {
   628  		t.Errorf("handler write error: %v", err)
   629  	}
   630  }
   631  
   632  func TestSessionID(t *testing.T) {
   633  	c1, c2, err := netPipe()
   634  	if err != nil {
   635  		t.Fatalf("netPipe: %v", err)
   636  	}
   637  	defer c1.Close()
   638  	defer c2.Close()
   639  
   640  	serverID := make(chan []byte, 1)
   641  	clientID := make(chan []byte, 1)
   642  
   643  	serverConf := &ServerConfig{
   644  		NoClientAuth: true,
   645  	}
   646  	serverConf.AddHostKey(testSigners["ecdsa"])
   647  	clientConf := &ClientConfig{
   648  		User: "user",
   649  	}
   650  
   651  	go func() {
   652  		conn, chans, reqs, err := NewServerConn(c1, serverConf)
   653  		if err != nil {
   654  			t.Fatalf("server handshake: %v", err)
   655  		}
   656  		serverID <- conn.SessionID()
   657  		go DiscardRequests(reqs)
   658  		for ch := range chans {
   659  			ch.Reject(Prohibited, "")
   660  		}
   661  	}()
   662  
   663  	go func() {
   664  		conn, chans, reqs, err := NewClientConn(c2, "", clientConf)
   665  		if err != nil {
   666  			t.Fatalf("client handshake: %v", err)
   667  		}
   668  		clientID <- conn.SessionID()
   669  		go DiscardRequests(reqs)
   670  		for ch := range chans {
   671  			ch.Reject(Prohibited, "")
   672  		}
   673  	}()
   674  
   675  	s := <-serverID
   676  	c := <-clientID
   677  	if bytes.Compare(s, c) != 0 {
   678  		t.Errorf("server session ID (%x) != client session ID (%x)", s, c)
   679  	} else if len(s) == 0 {
   680  		t.Errorf("client and server SessionID were empty.")
   681  	}
   682  }
   683  
   684  type noReadConn struct {
   685  	readSeen bool
   686  	net.Conn
   687  }
   688  
   689  func (c *noReadConn) Close() error {
   690  	return nil
   691  }
   692  
   693  func (c *noReadConn) Read(b []byte) (int, error) {
   694  	c.readSeen = true
   695  	return 0, errors.New("noReadConn error")
   696  }
   697  
   698  func TestInvalidServerConfiguration(t *testing.T) {
   699  	c1, c2, err := netPipe()
   700  	if err != nil {
   701  		t.Fatalf("netPipe: %v", err)
   702  	}
   703  	defer c1.Close()
   704  	defer c2.Close()
   705  
   706  	serveConn := noReadConn{Conn: c1}
   707  	serverConf := &ServerConfig{}
   708  
   709  	NewServerConn(&serveConn, serverConf)
   710  	if serveConn.readSeen {
   711  		t.Fatalf("NewServerConn attempted to Read() from Conn while configuration is missing host key")
   712  	}
   713  
   714  	serverConf.AddHostKey(testSigners["ecdsa"])
   715  
   716  	NewServerConn(&serveConn, serverConf)
   717  	if serveConn.readSeen {
   718  		t.Fatalf("NewServerConn attempted to Read() from Conn while configuration is missing authentication method")
   719  	}
   720  }
   721  
   722  func TestHostKeyAlgorithms(t *testing.T) {
   723  	serverConf := &ServerConfig{
   724  		NoClientAuth: true,
   725  	}
   726  	serverConf.AddHostKey(testSigners["rsa"])
   727  	serverConf.AddHostKey(testSigners["ecdsa"])
   728  
   729  	connect := func(clientConf *ClientConfig, want string) {
   730  		var alg string
   731  		clientConf.HostKeyCallback = func(h string, a net.Addr, key PublicKey) error {
   732  			alg = key.Type()
   733  			return nil
   734  		}
   735  		c1, c2, err := netPipe()
   736  		if err != nil {
   737  			t.Fatalf("netPipe: %v", err)
   738  		}
   739  		defer c1.Close()
   740  		defer c2.Close()
   741  
   742  		go NewServerConn(c1, serverConf)
   743  		_, _, _, err = NewClientConn(c2, "", clientConf)
   744  		if err != nil {
   745  			t.Fatalf("NewClientConn: %v", err)
   746  		}
   747  		if alg != want {
   748  			t.Errorf("selected key algorithm %s, want %s", alg, want)
   749  		}
   750  	}
   751  
   752  	// By default, we get the preferred algorithm, which is ECDSA 256.
   753  
   754  	clientConf := &ClientConfig{}
   755  	connect(clientConf, KeyAlgoECDSA256)
   756  
   757  	// Client asks for RSA explicitly.
   758  	clientConf.HostKeyAlgorithms = []string{KeyAlgoRSA}
   759  	connect(clientConf, KeyAlgoRSA)
   760  
   761  	c1, c2, err := netPipe()
   762  	if err != nil {
   763  		t.Fatalf("netPipe: %v", err)
   764  	}
   765  	defer c1.Close()
   766  	defer c2.Close()
   767  
   768  	go NewServerConn(c1, serverConf)
   769  	clientConf.HostKeyAlgorithms = []string{"nonexistent-hostkey-algo"}
   770  	_, _, _, err = NewClientConn(c2, "", clientConf)
   771  	if err == nil {
   772  		t.Fatal("succeeded connecting with unknown hostkey algorithm")
   773  	}
   774  }