github.com/devops-filetransfer/sshego@v7.0.4+incompatible/_vendor/golang.org/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  		HostKeyCallback: InsecureIgnoreHostKey(),
    64  	}
    65  
    66  	conn, chans, reqs, err := NewClientConn(c2, "", config)
    67  	if err != nil {
    68  		t.Fatalf("unable to dial remote side: %v", err)
    69  	}
    70  
    71  	return NewClient(conn, chans, reqs)
    72  }
    73  
    74  // Test a simple string is returned to session.Stdout.
    75  func TestSessionShell(t *testing.T) {
    76  	conn := dial(shellHandler, t)
    77  	defer conn.Close()
    78  	session, err := conn.NewSession()
    79  	if err != nil {
    80  		t.Fatalf("Unable to request new session: %v", err)
    81  	}
    82  	defer session.Close()
    83  	stdout := new(bytes.Buffer)
    84  	session.Stdout = stdout
    85  	if err := session.Shell(); err != nil {
    86  		t.Fatalf("Unable to execute command: %s", err)
    87  	}
    88  	if err := session.Wait(); err != nil {
    89  		t.Fatalf("Remote command did not exit cleanly: %v", err)
    90  	}
    91  	actual := stdout.String()
    92  	if actual != "golang" {
    93  		t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual)
    94  	}
    95  }
    96  
    97  // TODO(dfc) add support for Std{in,err}Pipe when the Server supports it.
    98  
    99  // Test a simple string is returned via StdoutPipe.
   100  func TestSessionStdoutPipe(t *testing.T) {
   101  	conn := dial(shellHandler, t)
   102  	defer conn.Close()
   103  	session, err := conn.NewSession()
   104  	if err != nil {
   105  		t.Fatalf("Unable to request new session: %v", err)
   106  	}
   107  	defer session.Close()
   108  	stdout, err := session.StdoutPipe()
   109  	if err != nil {
   110  		t.Fatalf("Unable to request StdoutPipe(): %v", err)
   111  	}
   112  	var buf bytes.Buffer
   113  	if err := session.Shell(); err != nil {
   114  		t.Fatalf("Unable to execute command: %v", err)
   115  	}
   116  	done := make(chan bool, 1)
   117  	go func() {
   118  		if _, err := io.Copy(&buf, stdout); err != nil {
   119  			t.Errorf("Copy of stdout failed: %v", err)
   120  		}
   121  		done <- true
   122  	}()
   123  	if err := session.Wait(); err != nil {
   124  		t.Fatalf("Remote command did not exit cleanly: %v", err)
   125  	}
   126  	<-done
   127  	actual := buf.String()
   128  	if actual != "golang" {
   129  		t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual)
   130  	}
   131  }
   132  
   133  // Test that a simple string is returned via the Output helper,
   134  // and that stderr is discarded.
   135  func TestSessionOutput(t *testing.T) {
   136  	conn := dial(fixedOutputHandler, t)
   137  	defer conn.Close()
   138  	session, err := conn.NewSession()
   139  	if err != nil {
   140  		t.Fatalf("Unable to request new session: %v", err)
   141  	}
   142  	defer session.Close()
   143  
   144  	buf, err := session.Output("") // cmd is ignored by fixedOutputHandler
   145  	if err != nil {
   146  		t.Error("Remote command did not exit cleanly:", err)
   147  	}
   148  	w := "this-is-stdout."
   149  	g := string(buf)
   150  	if g != w {
   151  		t.Error("Remote command did not return expected string:")
   152  		t.Logf("want %q", w)
   153  		t.Logf("got  %q", g)
   154  	}
   155  }
   156  
   157  // Test that both stdout and stderr are returned
   158  // via the CombinedOutput helper.
   159  func TestSessionCombinedOutput(t *testing.T) {
   160  	conn := dial(fixedOutputHandler, 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  
   168  	buf, err := session.CombinedOutput("") // cmd is ignored by fixedOutputHandler
   169  	if err != nil {
   170  		t.Error("Remote command did not exit cleanly:", err)
   171  	}
   172  	const stdout = "this-is-stdout."
   173  	const stderr = "this-is-stderr."
   174  	g := string(buf)
   175  	if g != stdout+stderr && g != stderr+stdout {
   176  		t.Error("Remote command did not return expected string:")
   177  		t.Logf("want %q, or %q", stdout+stderr, stderr+stdout)
   178  		t.Logf("got  %q", g)
   179  	}
   180  }
   181  
   182  // Test non-0 exit status is returned correctly.
   183  func TestExitStatusNonZero(t *testing.T) {
   184  	conn := dial(exitStatusNonZeroHandler, 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 command to fail but it didn't")
   197  	}
   198  	e, ok := err.(*ExitError)
   199  	if !ok {
   200  		t.Fatalf("expected *ExitError but got %T", err)
   201  	}
   202  	if e.ExitStatus() != 15 {
   203  		t.Fatalf("expected command to exit with 15 but got %v", e.ExitStatus())
   204  	}
   205  }
   206  
   207  // Test 0 exit status is returned correctly.
   208  func TestExitStatusZero(t *testing.T) {
   209  	conn := dial(exitStatusZeroHandler, t)
   210  	defer conn.Close()
   211  	session, err := conn.NewSession()
   212  	if err != nil {
   213  		t.Fatalf("Unable to request new session: %v", err)
   214  	}
   215  	defer session.Close()
   216  
   217  	if err := session.Shell(); err != nil {
   218  		t.Fatalf("Unable to execute command: %v", err)
   219  	}
   220  	err = session.Wait()
   221  	if err != nil {
   222  		t.Fatalf("expected nil but got %v", err)
   223  	}
   224  }
   225  
   226  // Test exit signal and status are both returned correctly.
   227  func TestExitSignalAndStatus(t *testing.T) {
   228  	conn := dial(exitSignalAndStatusHandler, t)
   229  	defer conn.Close()
   230  	session, err := conn.NewSession()
   231  	if err != nil {
   232  		t.Fatalf("Unable to request new session: %v", err)
   233  	}
   234  	defer session.Close()
   235  	if err := session.Shell(); err != nil {
   236  		t.Fatalf("Unable to execute command: %v", err)
   237  	}
   238  	err = session.Wait()
   239  	if err == nil {
   240  		t.Fatalf("expected command to fail but it didn't")
   241  	}
   242  	e, ok := err.(*ExitError)
   243  	if !ok {
   244  		t.Fatalf("expected *ExitError but got %T", err)
   245  	}
   246  	if e.Signal() != "TERM" || e.ExitStatus() != 15 {
   247  		t.Fatalf("expected command to exit with signal TERM and status 15 but got signal %s and status %v", e.Signal(), e.ExitStatus())
   248  	}
   249  }
   250  
   251  // Test exit signal and status are both returned correctly.
   252  func TestKnownExitSignalOnly(t *testing.T) {
   253  	conn := dial(exitSignalHandler, t)
   254  	defer conn.Close()
   255  	session, err := conn.NewSession()
   256  	if err != nil {
   257  		t.Fatalf("Unable to request new session: %v", err)
   258  	}
   259  	defer session.Close()
   260  	if err := session.Shell(); err != nil {
   261  		t.Fatalf("Unable to execute command: %v", err)
   262  	}
   263  	err = session.Wait()
   264  	if err == nil {
   265  		t.Fatalf("expected command to fail but it didn't")
   266  	}
   267  	e, ok := err.(*ExitError)
   268  	if !ok {
   269  		t.Fatalf("expected *ExitError but got %T", err)
   270  	}
   271  	if e.Signal() != "TERM" || e.ExitStatus() != 143 {
   272  		t.Fatalf("expected command to exit with signal TERM and status 143 but got signal %s and status %v", e.Signal(), e.ExitStatus())
   273  	}
   274  }
   275  
   276  // Test exit signal and status are both returned correctly.
   277  func TestUnknownExitSignal(t *testing.T) {
   278  	conn := dial(exitSignalUnknownHandler, t)
   279  	defer conn.Close()
   280  	session, err := conn.NewSession()
   281  	if err != nil {
   282  		t.Fatalf("Unable to request new session: %v", err)
   283  	}
   284  	defer session.Close()
   285  	if err := session.Shell(); err != nil {
   286  		t.Fatalf("Unable to execute command: %v", err)
   287  	}
   288  	err = session.Wait()
   289  	if err == nil {
   290  		t.Fatalf("expected command to fail but it didn't")
   291  	}
   292  	e, ok := err.(*ExitError)
   293  	if !ok {
   294  		t.Fatalf("expected *ExitError but got %T", err)
   295  	}
   296  	if e.Signal() != "SYS" || e.ExitStatus() != 128 {
   297  		t.Fatalf("expected command to exit with signal SYS and status 128 but got signal %s and status %v", e.Signal(), e.ExitStatus())
   298  	}
   299  }
   300  
   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  	if _, ok := err.(*ExitMissingError); !ok {
   317  		t.Fatalf("got %T want *ExitMissingError", err)
   318  	}
   319  }
   320  
   321  // windowTestBytes is the number of bytes that we'll send to the SSH server.
   322  const windowTestBytes = 16000 * 200
   323  
   324  // TestServerWindow writes random data to the server. The server is expected to echo
   325  // the same data back, which is compared against the original.
   326  func TestServerWindow(t *testing.T) {
   327  	origBuf := bytes.NewBuffer(make([]byte, 0, windowTestBytes))
   328  	io.CopyN(origBuf, crypto_rand.Reader, windowTestBytes)
   329  	origBytes := origBuf.Bytes()
   330  
   331  	conn := dial(echoHandler, t)
   332  	defer conn.Close()
   333  	session, err := conn.NewSession()
   334  	if err != nil {
   335  		t.Fatal(err)
   336  	}
   337  	defer session.Close()
   338  	result := make(chan []byte)
   339  
   340  	go func() {
   341  		defer close(result)
   342  		echoedBuf := bytes.NewBuffer(make([]byte, 0, windowTestBytes))
   343  		serverStdout, err := session.StdoutPipe()
   344  		if err != nil {
   345  			t.Errorf("StdoutPipe failed: %v", err)
   346  			return
   347  		}
   348  		n, err := copyNRandomly("stdout", echoedBuf, serverStdout, windowTestBytes)
   349  		if err != nil && err != io.EOF {
   350  			t.Errorf("Read only %d bytes from server, expected %d: %v", n, windowTestBytes, err)
   351  		}
   352  		result <- echoedBuf.Bytes()
   353  	}()
   354  
   355  	serverStdin, err := session.StdinPipe()
   356  	if err != nil {
   357  		t.Fatalf("StdinPipe failed: %v", err)
   358  	}
   359  	written, err := copyNRandomly("stdin", serverStdin, origBuf, windowTestBytes)
   360  	if err != nil {
   361  		t.Fatalf("failed to copy origBuf to serverStdin: %v", err)
   362  	}
   363  	if written != windowTestBytes {
   364  		t.Fatalf("Wrote only %d of %d bytes to server", written, windowTestBytes)
   365  	}
   366  
   367  	echoedBytes := <-result
   368  
   369  	if !bytes.Equal(origBytes, echoedBytes) {
   370  		t.Fatalf("Echoed buffer differed from original, orig %d, echoed %d", len(origBytes), len(echoedBytes))
   371  	}
   372  }
   373  
   374  // Verify the client can handle a keepalive packet from the server.
   375  func TestClientHandlesKeepalives(t *testing.T) {
   376  	conn := dial(channelKeepaliveSender, t)
   377  	defer conn.Close()
   378  	session, err := conn.NewSession()
   379  	if err != nil {
   380  		t.Fatal(err)
   381  	}
   382  	defer session.Close()
   383  	if err := session.Shell(); err != nil {
   384  		t.Fatalf("Unable to execute command: %v", err)
   385  	}
   386  	err = session.Wait()
   387  	if err != nil {
   388  		t.Fatalf("expected nil but got: %v", err)
   389  	}
   390  }
   391  
   392  type exitStatusMsg struct {
   393  	Status uint32
   394  }
   395  
   396  type exitSignalMsg struct {
   397  	Signal     string
   398  	CoreDumped bool
   399  	Errmsg     string
   400  	Lang       string
   401  }
   402  
   403  func handleTerminalRequests(in <-chan *Request) {
   404  	for req := range in {
   405  		ok := false
   406  		switch req.Type {
   407  		case "shell":
   408  			ok = true
   409  			if len(req.Payload) > 0 {
   410  				// We don't accept any commands, only the default shell.
   411  				ok = false
   412  			}
   413  		case "env":
   414  			ok = true
   415  		}
   416  		req.Reply(ok, nil)
   417  	}
   418  }
   419  
   420  func newServerShell(ch Channel, in <-chan *Request, prompt string) *terminal.Terminal {
   421  	term := terminal.NewTerminal(ch, prompt)
   422  	go handleTerminalRequests(in)
   423  	return term
   424  }
   425  
   426  func exitStatusZeroHandler(ch Channel, in <-chan *Request, t *testing.T) {
   427  	defer ch.Close()
   428  	// this string is returned to stdout
   429  	shell := newServerShell(ch, in, "> ")
   430  	readLine(shell, t)
   431  	sendStatus(0, ch, t)
   432  }
   433  
   434  func exitStatusNonZeroHandler(ch Channel, in <-chan *Request, t *testing.T) {
   435  	defer ch.Close()
   436  	shell := newServerShell(ch, in, "> ")
   437  	readLine(shell, t)
   438  	sendStatus(15, ch, t)
   439  }
   440  
   441  func exitSignalAndStatusHandler(ch Channel, in <-chan *Request, t *testing.T) {
   442  	defer ch.Close()
   443  	shell := newServerShell(ch, in, "> ")
   444  	readLine(shell, t)
   445  	sendStatus(15, ch, t)
   446  	sendSignal("TERM", ch, t)
   447  }
   448  
   449  func exitSignalHandler(ch Channel, in <-chan *Request, t *testing.T) {
   450  	defer ch.Close()
   451  	shell := newServerShell(ch, in, "> ")
   452  	readLine(shell, t)
   453  	sendSignal("TERM", ch, t)
   454  }
   455  
   456  func exitSignalUnknownHandler(ch Channel, in <-chan *Request, t *testing.T) {
   457  	defer ch.Close()
   458  	shell := newServerShell(ch, in, "> ")
   459  	readLine(shell, t)
   460  	sendSignal("SYS", ch, t)
   461  }
   462  
   463  func exitWithoutSignalOrStatus(ch Channel, in <-chan *Request, t *testing.T) {
   464  	defer ch.Close()
   465  	shell := newServerShell(ch, in, "> ")
   466  	readLine(shell, t)
   467  }
   468  
   469  func shellHandler(ch Channel, in <-chan *Request, t *testing.T) {
   470  	defer ch.Close()
   471  	// this string is returned to stdout
   472  	shell := newServerShell(ch, in, "golang")
   473  	readLine(shell, t)
   474  	sendStatus(0, ch, t)
   475  }
   476  
   477  // Ignores the command, writes fixed strings to stderr and stdout.
   478  // Strings are "this-is-stdout." and "this-is-stderr.".
   479  func fixedOutputHandler(ch Channel, in <-chan *Request, t *testing.T) {
   480  	defer ch.Close()
   481  	_, err := ch.Read(nil)
   482  
   483  	req, ok := <-in
   484  	if !ok {
   485  		t.Fatalf("error: expected channel request, got: %#v", err)
   486  		return
   487  	}
   488  
   489  	// ignore request, always send some text
   490  	req.Reply(true, nil)
   491  
   492  	_, err = io.WriteString(ch, "this-is-stdout.")
   493  	if err != nil {
   494  		t.Fatalf("error writing on server: %v", err)
   495  	}
   496  	_, err = io.WriteString(ch.Stderr(), "this-is-stderr.")
   497  	if err != nil {
   498  		t.Fatalf("error writing on server: %v", err)
   499  	}
   500  	sendStatus(0, ch, t)
   501  }
   502  
   503  func readLine(shell *terminal.Terminal, t *testing.T) {
   504  	if _, err := shell.ReadLine(); err != nil && err != io.EOF {
   505  		t.Errorf("unable to read line: %v", err)
   506  	}
   507  }
   508  
   509  func sendStatus(status uint32, ch Channel, t *testing.T) {
   510  	msg := exitStatusMsg{
   511  		Status: status,
   512  	}
   513  	if _, err := ch.SendRequest("exit-status", false, Marshal(&msg)); err != nil {
   514  		t.Errorf("unable to send status: %v", err)
   515  	}
   516  }
   517  
   518  func sendSignal(signal string, ch Channel, t *testing.T) {
   519  	sig := exitSignalMsg{
   520  		Signal:     signal,
   521  		CoreDumped: false,
   522  		Errmsg:     "Process terminated",
   523  		Lang:       "en-GB-oed",
   524  	}
   525  	if _, err := ch.SendRequest("exit-signal", false, Marshal(&sig)); err != nil {
   526  		t.Errorf("unable to send signal: %v", err)
   527  	}
   528  }
   529  
   530  func discardHandler(ch Channel, t *testing.T) {
   531  	defer ch.Close()
   532  	io.Copy(ioutil.Discard, ch)
   533  }
   534  
   535  func echoHandler(ch Channel, in <-chan *Request, t *testing.T) {
   536  	defer ch.Close()
   537  	if n, err := copyNRandomly("echohandler", ch, ch, windowTestBytes); err != nil {
   538  		t.Errorf("short write, wrote %d, expected %d: %v ", n, windowTestBytes, err)
   539  	}
   540  }
   541  
   542  // copyNRandomly copies n bytes from src to dst. It uses a variable, and random,
   543  // buffer size to exercise more code paths.
   544  func copyNRandomly(title string, dst io.Writer, src io.Reader, n int) (int, error) {
   545  	var (
   546  		buf       = make([]byte, 32*1024)
   547  		written   int
   548  		remaining = n
   549  	)
   550  	for remaining > 0 {
   551  		l := rand.Intn(1 << 15)
   552  		if remaining < l {
   553  			l = remaining
   554  		}
   555  		nr, er := src.Read(buf[:l])
   556  		nw, ew := dst.Write(buf[:nr])
   557  		remaining -= nw
   558  		written += nw
   559  		if ew != nil {
   560  			return written, ew
   561  		}
   562  		if nr != nw {
   563  			return written, io.ErrShortWrite
   564  		}
   565  		if er != nil && er != io.EOF {
   566  			return written, er
   567  		}
   568  	}
   569  	return written, nil
   570  }
   571  
   572  func channelKeepaliveSender(ch Channel, in <-chan *Request, t *testing.T) {
   573  	defer ch.Close()
   574  	shell := newServerShell(ch, in, "> ")
   575  	readLine(shell, t)
   576  	if _, err := ch.SendRequest("keepalive@openssh.com", true, nil); err != nil {
   577  		t.Errorf("unable to send channel keepalive request: %v", err)
   578  	}
   579  	sendStatus(0, ch, t)
   580  }
   581  
   582  func TestClientWriteEOF(t *testing.T) {
   583  	conn := dial(simpleEchoHandler, t)
   584  	defer conn.Close()
   585  
   586  	session, err := conn.NewSession()
   587  	if err != nil {
   588  		t.Fatal(err)
   589  	}
   590  	defer session.Close()
   591  	stdin, err := session.StdinPipe()
   592  	if err != nil {
   593  		t.Fatalf("StdinPipe failed: %v", err)
   594  	}
   595  	stdout, err := session.StdoutPipe()
   596  	if err != nil {
   597  		t.Fatalf("StdoutPipe failed: %v", err)
   598  	}
   599  
   600  	data := []byte(`0000`)
   601  	_, err = stdin.Write(data)
   602  	if err != nil {
   603  		t.Fatalf("Write failed: %v", err)
   604  	}
   605  	stdin.Close()
   606  
   607  	res, err := ioutil.ReadAll(stdout)
   608  	if err != nil {
   609  		t.Fatalf("Read failed: %v", err)
   610  	}
   611  
   612  	if !bytes.Equal(data, res) {
   613  		t.Fatalf("Read differed from write, wrote: %v, read: %v", data, res)
   614  	}
   615  }
   616  
   617  func simpleEchoHandler(ch Channel, in <-chan *Request, t *testing.T) {
   618  	defer ch.Close()
   619  	data, err := ioutil.ReadAll(ch)
   620  	if err != nil {
   621  		t.Errorf("handler read error: %v", err)
   622  	}
   623  	_, err = ch.Write(data)
   624  	if err != nil {
   625  		t.Errorf("handler write error: %v", err)
   626  	}
   627  }
   628  
   629  func TestSessionID(t *testing.T) {
   630  	c1, c2, err := netPipe()
   631  	if err != nil {
   632  		t.Fatalf("netPipe: %v", err)
   633  	}
   634  	defer c1.Close()
   635  	defer c2.Close()
   636  
   637  	serverID := make(chan []byte, 1)
   638  	clientID := make(chan []byte, 1)
   639  
   640  	serverConf := &ServerConfig{
   641  		NoClientAuth: true,
   642  	}
   643  	serverConf.AddHostKey(testSigners["ecdsa"])
   644  	clientConf := &ClientConfig{
   645  		HostKeyCallback: InsecureIgnoreHostKey(),
   646  		User:            "user",
   647  	}
   648  
   649  	go func() {
   650  		conn, chans, reqs, err := NewServerConn(c1, serverConf)
   651  		if err != nil {
   652  			t.Fatalf("server handshake: %v", err)
   653  		}
   654  		serverID <- conn.SessionID()
   655  		go DiscardRequests(reqs)
   656  		for ch := range chans {
   657  			ch.Reject(Prohibited, "")
   658  		}
   659  	}()
   660  
   661  	go func() {
   662  		conn, chans, reqs, err := NewClientConn(c2, "", clientConf)
   663  		if err != nil {
   664  			t.Fatalf("client handshake: %v", err)
   665  		}
   666  		clientID <- conn.SessionID()
   667  		go DiscardRequests(reqs)
   668  		for ch := range chans {
   669  			ch.Reject(Prohibited, "")
   670  		}
   671  	}()
   672  
   673  	s := <-serverID
   674  	c := <-clientID
   675  	if bytes.Compare(s, c) != 0 {
   676  		t.Errorf("server session ID (%x) != client session ID (%x)", s, c)
   677  	} else if len(s) == 0 {
   678  		t.Errorf("client and server SessionID were empty.")
   679  	}
   680  }
   681  
   682  type noReadConn struct {
   683  	readSeen bool
   684  	net.Conn
   685  }
   686  
   687  func (c *noReadConn) Close() error {
   688  	return nil
   689  }
   690  
   691  func (c *noReadConn) Read(b []byte) (int, error) {
   692  	c.readSeen = true
   693  	return 0, errors.New("noReadConn error")
   694  }
   695  
   696  func TestInvalidServerConfiguration(t *testing.T) {
   697  	c1, c2, err := netPipe()
   698  	if err != nil {
   699  		t.Fatalf("netPipe: %v", err)
   700  	}
   701  	defer c1.Close()
   702  	defer c2.Close()
   703  
   704  	serveConn := noReadConn{Conn: c1}
   705  	serverConf := &ServerConfig{}
   706  
   707  	NewServerConn(&serveConn, serverConf)
   708  	if serveConn.readSeen {
   709  		t.Fatalf("NewServerConn attempted to Read() from Conn while configuration is missing host key")
   710  	}
   711  
   712  	serverConf.AddHostKey(testSigners["ecdsa"])
   713  
   714  	NewServerConn(&serveConn, serverConf)
   715  	if serveConn.readSeen {
   716  		t.Fatalf("NewServerConn attempted to Read() from Conn while configuration is missing authentication method")
   717  	}
   718  }
   719  
   720  func TestHostKeyAlgorithms(t *testing.T) {
   721  	serverConf := &ServerConfig{
   722  		NoClientAuth: true,
   723  	}
   724  	serverConf.AddHostKey(testSigners["rsa"])
   725  	serverConf.AddHostKey(testSigners["ecdsa"])
   726  
   727  	connect := func(clientConf *ClientConfig, want string) {
   728  		var alg string
   729  		clientConf.HostKeyCallback = func(h string, a net.Addr, key PublicKey) error {
   730  			alg = key.Type()
   731  			return nil
   732  		}
   733  		c1, c2, err := netPipe()
   734  		if err != nil {
   735  			t.Fatalf("netPipe: %v", err)
   736  		}
   737  		defer c1.Close()
   738  		defer c2.Close()
   739  
   740  		go NewServerConn(c1, serverConf)
   741  		_, _, _, err = NewClientConn(c2, "", clientConf)
   742  		if err != nil {
   743  			t.Fatalf("NewClientConn: %v", err)
   744  		}
   745  		if alg != want {
   746  			t.Errorf("selected key algorithm %s, want %s", alg, want)
   747  		}
   748  	}
   749  
   750  	// By default, we get the preferred algorithm, which is ECDSA 256.
   751  
   752  	clientConf := &ClientConfig{
   753  		HostKeyCallback: InsecureIgnoreHostKey(),
   754  	}
   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  }