github.com/maenmax/kairep@v0.0.0-20210218001208-55bf3df36788/src/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  	}
    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  func TestExitWithoutStatusOrSignal(t *testing.T) {
   301  	conn := dial(exitWithoutSignalOrStatus, t)
   302  	defer conn.Close()
   303  	session, err := conn.NewSession()
   304  	if err != nil {
   305  		t.Fatalf("Unable to request new session: %v", err)
   306  	}
   307  	defer session.Close()
   308  	if err := session.Shell(); err != nil {
   309  		t.Fatalf("Unable to execute command: %v", err)
   310  	}
   311  	err = session.Wait()
   312  	if err == nil {
   313  		t.Fatalf("expected command to fail but it didn't")
   314  	}
   315  	if _, ok := err.(*ExitMissingError); !ok {
   316  		t.Fatalf("got %T want *ExitMissingError", err)
   317  	}
   318  }
   319  
   320  // windowTestBytes is the number of bytes that we'll send to the SSH server.
   321  const 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.
   325  func 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 {
   360  		t.Fatalf("failed to copy origBuf to serverStdin: %v", err)
   361  	}
   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  
   373  // Verify the client can handle a keepalive packet from the server.
   374  func 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  
   391  type exitStatusMsg struct {
   392  	Status uint32
   393  }
   394  
   395  type exitSignalMsg struct {
   396  	Signal     string
   397  	CoreDumped bool
   398  	Errmsg     string
   399  	Lang       string
   400  }
   401  
   402  func 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)
   416  	}
   417  }
   418  
   419  func 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  
   425  func exitStatusZeroHandler(ch Channel, in <-chan *Request, t *testing.T) {
   426  	defer ch.Close()
   427  	// this string is returned to stdout
   428  	shell := newServerShell(ch, in, "> ")
   429  	readLine(shell, t)
   430  	sendStatus(0, ch, t)
   431  }
   432  
   433  func exitStatusNonZeroHandler(ch Channel, in <-chan *Request, t *testing.T) {
   434  	defer ch.Close()
   435  	shell := newServerShell(ch, in, "> ")
   436  	readLine(shell, t)
   437  	sendStatus(15, ch, t)
   438  }
   439  
   440  func exitSignalAndStatusHandler(ch Channel, in <-chan *Request, t *testing.T) {
   441  	defer ch.Close()
   442  	shell := newServerShell(ch, in, "> ")
   443  	readLine(shell, t)
   444  	sendStatus(15, ch, t)
   445  	sendSignal("TERM", ch, t)
   446  }
   447  
   448  func exitSignalHandler(ch Channel, in <-chan *Request, t *testing.T) {
   449  	defer ch.Close()
   450  	shell := newServerShell(ch, in, "> ")
   451  	readLine(shell, t)
   452  	sendSignal("TERM", ch, t)
   453  }
   454  
   455  func exitSignalUnknownHandler(ch Channel, in <-chan *Request, t *testing.T) {
   456  	defer ch.Close()
   457  	shell := newServerShell(ch, in, "> ")
   458  	readLine(shell, t)
   459  	sendSignal("SYS", ch, t)
   460  }
   461  
   462  func exitWithoutSignalOrStatus(ch Channel, in <-chan *Request, t *testing.T) {
   463  	defer ch.Close()
   464  	shell := newServerShell(ch, in, "> ")
   465  	readLine(shell, t)
   466  }
   467  
   468  func shellHandler(ch Channel, in <-chan *Request, t *testing.T) {
   469  	defer ch.Close()
   470  	// this string is returned to stdout
   471  	shell := newServerShell(ch, in, "golang")
   472  	readLine(shell, t)
   473  	sendStatus(0, ch, t)
   474  }
   475  
   476  // Ignores the command, writes fixed strings to stderr and stdout.
   477  // Strings are "this-is-stdout." and "this-is-stderr.".
   478  func fixedOutputHandler(ch Channel, in <-chan *Request, t *testing.T) {
   479  	defer ch.Close()
   480  	_, err := ch.Read(nil)
   481  
   482  	req, ok := <-in
   483  	if !ok {
   484  		t.Fatalf("error: expected channel request, got: %#v", err)
   485  		return
   486  	}
   487  
   488  	// ignore request, always send some text
   489  	req.Reply(true, nil)
   490  
   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  
   502  func readLine(shell *terminal.Terminal, t *testing.T) {
   503  	if _, err := shell.ReadLine(); err != nil && err != io.EOF {
   504  		t.Errorf("unable to read line: %v", err)
   505  	}
   506  }
   507  
   508  func sendStatus(status uint32, ch Channel, t *testing.T) {
   509  	msg := exitStatusMsg{
   510  		Status: status,
   511  	}
   512  	if _, err := ch.SendRequest("exit-status", false, Marshal(&msg)); err != nil {
   513  		t.Errorf("unable to send status: %v", err)
   514  	}
   515  }
   516  
   517  func sendSignal(signal string, ch Channel, t *testing.T) {
   518  	sig := exitSignalMsg{
   519  		Signal:     signal,
   520  		CoreDumped: false,
   521  		Errmsg:     "Process terminated",
   522  		Lang:       "en-GB-oed",
   523  	}
   524  	if _, err := ch.SendRequest("exit-signal", false, Marshal(&sig)); err != nil {
   525  		t.Errorf("unable to send signal: %v", err)
   526  	}
   527  }
   528  
   529  func discardHandler(ch Channel, t *testing.T) {
   530  	defer ch.Close()
   531  	io.Copy(ioutil.Discard, ch)
   532  }
   533  
   534  func echoHandler(ch Channel, in <-chan *Request, t *testing.T) {
   535  	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.
   543  func 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  }
   570  
   571  func channelKeepaliveSender(ch Channel, in <-chan *Request, t *testing.T) {
   572  	defer ch.Close()
   573  	shell := newServerShell(ch, in, "> ")
   574  	readLine(shell, t)
   575  	if _, err := ch.SendRequest("keepalive@openssh.com", true, nil); err != nil {
   576  		t.Errorf("unable to send channel keepalive request: %v", err)
   577  	}
   578  	sendStatus(0, ch, t)
   579  }
   580  
   581  func 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  
   616  func 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  }
   627  
   628  func 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  }
   679  
   680  type noReadConn struct {
   681  	readSeen bool
   682  	net.Conn
   683  }
   684  
   685  func (c *noReadConn) Close() error {
   686  	return nil
   687  }
   688  
   689  func (c *noReadConn) Read(b []byte) (int, error) {
   690  	c.readSeen = true
   691  	return 0, errors.New("noReadConn error")
   692  }
   693  
   694  func 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  }
   717  
   718  func 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  }