github.com/deis/deis@v1.13.5-0.20170519182049-1d9e59fbdbfc/Godeps/_workspace/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  	"io"
    13  	"io/ioutil"
    14  	"math/rand"
    15  	"testing"
    16  
    17  	"golang.org/x/crypto/ssh/terminal"
    18  )
    19  
    20  type serverType func(Channel, <-chan *Request, *testing.T)
    21  
    22  // dial constructs a new test server and returns a *ClientConn.
    23  func dial(handler serverType, t *testing.T) *Client {
    24  	c1, c2, err := netPipe()
    25  	if err != nil {
    26  		t.Fatalf("netPipe: %v", err)
    27  	}
    28  
    29  	go func() {
    30  		defer c1.Close()
    31  		conf := ServerConfig{
    32  			NoClientAuth: true,
    33  		}
    34  		conf.AddHostKey(testSigners["rsa"])
    35  
    36  		_, chans, reqs, err := NewServerConn(c1, &conf)
    37  		if err != nil {
    38  			t.Fatalf("Unable to handshake: %v", err)
    39  		}
    40  		go DiscardRequests(reqs)
    41  
    42  		for newCh := range chans {
    43  			if newCh.ChannelType() != "session" {
    44  				newCh.Reject(UnknownChannelType, "unknown channel type")
    45  				continue
    46  			}
    47  
    48  			ch, inReqs, err := newCh.Accept()
    49  			if err != nil {
    50  				t.Errorf("Accept: %v", err)
    51  				continue
    52  			}
    53  			go func() {
    54  				handler(ch, inReqs, t)
    55  			}()
    56  		}
    57  	}()
    58  
    59  	config := &ClientConfig{
    60  		User: "testuser",
    61  	}
    62  
    63  	conn, chans, reqs, err := NewClientConn(c2, "", config)
    64  	if err != nil {
    65  		t.Fatalf("unable to dial remote side: %v", err)
    66  	}
    67  
    68  	return NewClient(conn, chans, reqs)
    69  }
    70  
    71  // Test a simple string is returned to session.Stdout.
    72  func TestSessionShell(t *testing.T) {
    73  	conn := dial(shellHandler, t)
    74  	defer conn.Close()
    75  	session, err := conn.NewSession()
    76  	if err != nil {
    77  		t.Fatalf("Unable to request new session: %v", err)
    78  	}
    79  	defer session.Close()
    80  	stdout := new(bytes.Buffer)
    81  	session.Stdout = stdout
    82  	if err := session.Shell(); err != nil {
    83  		t.Fatalf("Unable to execute command: %s", err)
    84  	}
    85  	if err := session.Wait(); err != nil {
    86  		t.Fatalf("Remote command did not exit cleanly: %v", err)
    87  	}
    88  	actual := stdout.String()
    89  	if actual != "golang" {
    90  		t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual)
    91  	}
    92  }
    93  
    94  // TODO(dfc) add support for Std{in,err}Pipe when the Server supports it.
    95  
    96  // Test a simple string is returned via StdoutPipe.
    97  func TestSessionStdoutPipe(t *testing.T) {
    98  	conn := dial(shellHandler, t)
    99  	defer conn.Close()
   100  	session, err := conn.NewSession()
   101  	if err != nil {
   102  		t.Fatalf("Unable to request new session: %v", err)
   103  	}
   104  	defer session.Close()
   105  	stdout, err := session.StdoutPipe()
   106  	if err != nil {
   107  		t.Fatalf("Unable to request StdoutPipe(): %v", err)
   108  	}
   109  	var buf bytes.Buffer
   110  	if err := session.Shell(); err != nil {
   111  		t.Fatalf("Unable to execute command: %v", err)
   112  	}
   113  	done := make(chan bool, 1)
   114  	go func() {
   115  		if _, err := io.Copy(&buf, stdout); err != nil {
   116  			t.Errorf("Copy of stdout failed: %v", err)
   117  		}
   118  		done <- true
   119  	}()
   120  	if err := session.Wait(); err != nil {
   121  		t.Fatalf("Remote command did not exit cleanly: %v", err)
   122  	}
   123  	<-done
   124  	actual := buf.String()
   125  	if actual != "golang" {
   126  		t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual)
   127  	}
   128  }
   129  
   130  // Test that a simple string is returned via the Output helper,
   131  // and that stderr is discarded.
   132  func TestSessionOutput(t *testing.T) {
   133  	conn := dial(fixedOutputHandler, t)
   134  	defer conn.Close()
   135  	session, err := conn.NewSession()
   136  	if err != nil {
   137  		t.Fatalf("Unable to request new session: %v", err)
   138  	}
   139  	defer session.Close()
   140  
   141  	buf, err := session.Output("") // cmd is ignored by fixedOutputHandler
   142  	if err != nil {
   143  		t.Error("Remote command did not exit cleanly:", err)
   144  	}
   145  	w := "this-is-stdout."
   146  	g := string(buf)
   147  	if g != w {
   148  		t.Error("Remote command did not return expected string:")
   149  		t.Logf("want %q", w)
   150  		t.Logf("got  %q", g)
   151  	}
   152  }
   153  
   154  // Test that both stdout and stderr are returned
   155  // via the CombinedOutput helper.
   156  func TestSessionCombinedOutput(t *testing.T) {
   157  	conn := dial(fixedOutputHandler, t)
   158  	defer conn.Close()
   159  	session, err := conn.NewSession()
   160  	if err != nil {
   161  		t.Fatalf("Unable to request new session: %v", err)
   162  	}
   163  	defer session.Close()
   164  
   165  	buf, err := session.CombinedOutput("") // cmd is ignored by fixedOutputHandler
   166  	if err != nil {
   167  		t.Error("Remote command did not exit cleanly:", err)
   168  	}
   169  	const stdout = "this-is-stdout."
   170  	const stderr = "this-is-stderr."
   171  	g := string(buf)
   172  	if g != stdout+stderr && g != stderr+stdout {
   173  		t.Error("Remote command did not return expected string:")
   174  		t.Logf("want %q, or %q", stdout+stderr, stderr+stdout)
   175  		t.Logf("got  %q", g)
   176  	}
   177  }
   178  
   179  // Test non-0 exit status is returned correctly.
   180  func TestExitStatusNonZero(t *testing.T) {
   181  	conn := dial(exitStatusNonZeroHandler, t)
   182  	defer conn.Close()
   183  	session, err := conn.NewSession()
   184  	if err != nil {
   185  		t.Fatalf("Unable to request new session: %v", err)
   186  	}
   187  	defer session.Close()
   188  	if err := session.Shell(); err != nil {
   189  		t.Fatalf("Unable to execute command: %v", err)
   190  	}
   191  	err = session.Wait()
   192  	if err == nil {
   193  		t.Fatalf("expected command to fail but it didn't")
   194  	}
   195  	e, ok := err.(*ExitError)
   196  	if !ok {
   197  		t.Fatalf("expected *ExitError but got %T", err)
   198  	}
   199  	if e.ExitStatus() != 15 {
   200  		t.Fatalf("expected command to exit with 15 but got %v", e.ExitStatus())
   201  	}
   202  }
   203  
   204  // Test 0 exit status is returned correctly.
   205  func TestExitStatusZero(t *testing.T) {
   206  	conn := dial(exitStatusZeroHandler, t)
   207  	defer conn.Close()
   208  	session, err := conn.NewSession()
   209  	if err != nil {
   210  		t.Fatalf("Unable to request new session: %v", err)
   211  	}
   212  	defer session.Close()
   213  
   214  	if err := session.Shell(); err != nil {
   215  		t.Fatalf("Unable to execute command: %v", err)
   216  	}
   217  	err = session.Wait()
   218  	if err != nil {
   219  		t.Fatalf("expected nil but got %v", err)
   220  	}
   221  }
   222  
   223  // Test exit signal and status are both returned correctly.
   224  func TestExitSignalAndStatus(t *testing.T) {
   225  	conn := dial(exitSignalAndStatusHandler, t)
   226  	defer conn.Close()
   227  	session, err := conn.NewSession()
   228  	if err != nil {
   229  		t.Fatalf("Unable to request new session: %v", err)
   230  	}
   231  	defer session.Close()
   232  	if err := session.Shell(); err != nil {
   233  		t.Fatalf("Unable to execute command: %v", err)
   234  	}
   235  	err = session.Wait()
   236  	if err == nil {
   237  		t.Fatalf("expected command to fail but it didn't")
   238  	}
   239  	e, ok := err.(*ExitError)
   240  	if !ok {
   241  		t.Fatalf("expected *ExitError but got %T", err)
   242  	}
   243  	if e.Signal() != "TERM" || e.ExitStatus() != 15 {
   244  		t.Fatalf("expected command to exit with signal TERM and status 15 but got signal %s and status %v", e.Signal(), e.ExitStatus())
   245  	}
   246  }
   247  
   248  // Test exit signal and status are both returned correctly.
   249  func TestKnownExitSignalOnly(t *testing.T) {
   250  	conn := dial(exitSignalHandler, t)
   251  	defer conn.Close()
   252  	session, err := conn.NewSession()
   253  	if err != nil {
   254  		t.Fatalf("Unable to request new session: %v", err)
   255  	}
   256  	defer session.Close()
   257  	if err := session.Shell(); err != nil {
   258  		t.Fatalf("Unable to execute command: %v", err)
   259  	}
   260  	err = session.Wait()
   261  	if err == nil {
   262  		t.Fatalf("expected command to fail but it didn't")
   263  	}
   264  	e, ok := err.(*ExitError)
   265  	if !ok {
   266  		t.Fatalf("expected *ExitError but got %T", err)
   267  	}
   268  	if e.Signal() != "TERM" || e.ExitStatus() != 143 {
   269  		t.Fatalf("expected command to exit with signal TERM and status 143 but got signal %s and status %v", e.Signal(), e.ExitStatus())
   270  	}
   271  }
   272  
   273  // Test exit signal and status are both returned correctly.
   274  func TestUnknownExitSignal(t *testing.T) {
   275  	conn := dial(exitSignalUnknownHandler, t)
   276  	defer conn.Close()
   277  	session, err := conn.NewSession()
   278  	if err != nil {
   279  		t.Fatalf("Unable to request new session: %v", err)
   280  	}
   281  	defer session.Close()
   282  	if err := session.Shell(); err != nil {
   283  		t.Fatalf("Unable to execute command: %v", err)
   284  	}
   285  	err = session.Wait()
   286  	if err == nil {
   287  		t.Fatalf("expected command to fail but it didn't")
   288  	}
   289  	e, ok := err.(*ExitError)
   290  	if !ok {
   291  		t.Fatalf("expected *ExitError but got %T", err)
   292  	}
   293  	if e.Signal() != "SYS" || e.ExitStatus() != 128 {
   294  		t.Fatalf("expected command to exit with signal SYS and status 128 but got signal %s and status %v", e.Signal(), e.ExitStatus())
   295  	}
   296  }
   297  
   298  // Test WaitMsg is not returned if the channel closes abruptly.
   299  func TestExitWithoutStatusOrSignal(t *testing.T) {
   300  	conn := dial(exitWithoutSignalOrStatus, t)
   301  	defer conn.Close()
   302  	session, err := conn.NewSession()
   303  	if err != nil {
   304  		t.Fatalf("Unable to request new session: %v", err)
   305  	}
   306  	defer session.Close()
   307  	if err := session.Shell(); err != nil {
   308  		t.Fatalf("Unable to execute command: %v", err)
   309  	}
   310  	err = session.Wait()
   311  	if err == nil {
   312  		t.Fatalf("expected command to fail but it didn't")
   313  	}
   314  	_, ok := err.(*ExitError)
   315  	if ok {
   316  		// you can't actually test for errors.errorString
   317  		// because it's not exported.
   318  		t.Fatalf("expected *errorString but got %T", err)
   319  	}
   320  }
   321  
   322  // windowTestBytes is the number of bytes that we'll send to the SSH server.
   323  const windowTestBytes = 16000 * 200
   324  
   325  // TestServerWindow writes random data to the server. The server is expected to echo
   326  // the same data back, which is compared against the original.
   327  func TestServerWindow(t *testing.T) {
   328  	origBuf := bytes.NewBuffer(make([]byte, 0, windowTestBytes))
   329  	io.CopyN(origBuf, crypto_rand.Reader, windowTestBytes)
   330  	origBytes := origBuf.Bytes()
   331  
   332  	conn := dial(echoHandler, t)
   333  	defer conn.Close()
   334  	session, err := conn.NewSession()
   335  	if err != nil {
   336  		t.Fatal(err)
   337  	}
   338  	defer session.Close()
   339  	result := make(chan []byte)
   340  
   341  	go func() {
   342  		defer close(result)
   343  		echoedBuf := bytes.NewBuffer(make([]byte, 0, windowTestBytes))
   344  		serverStdout, err := session.StdoutPipe()
   345  		if err != nil {
   346  			t.Errorf("StdoutPipe failed: %v", err)
   347  			return
   348  		}
   349  		n, err := copyNRandomly("stdout", echoedBuf, serverStdout, windowTestBytes)
   350  		if err != nil && err != io.EOF {
   351  			t.Errorf("Read only %d bytes from server, expected %d: %v", n, windowTestBytes, err)
   352  		}
   353  		result <- echoedBuf.Bytes()
   354  	}()
   355  
   356  	serverStdin, err := session.StdinPipe()
   357  	if err != nil {
   358  		t.Fatalf("StdinPipe failed: %v", err)
   359  	}
   360  	written, err := copyNRandomly("stdin", serverStdin, origBuf, windowTestBytes)
   361  	if err != nil {
   362  		t.Fatalf("failed to copy origBuf to serverStdin: %v", err)
   363  	}
   364  	if written != windowTestBytes {
   365  		t.Fatalf("Wrote only %d of %d bytes to server", written, windowTestBytes)
   366  	}
   367  
   368  	echoedBytes := <-result
   369  
   370  	if !bytes.Equal(origBytes, echoedBytes) {
   371  		t.Fatalf("Echoed buffer differed from original, orig %d, echoed %d", len(origBytes), len(echoedBytes))
   372  	}
   373  }
   374  
   375  // Verify the client can handle a keepalive packet from the server.
   376  func TestClientHandlesKeepalives(t *testing.T) {
   377  	conn := dial(channelKeepaliveSender, t)
   378  	defer conn.Close()
   379  	session, err := conn.NewSession()
   380  	if err != nil {
   381  		t.Fatal(err)
   382  	}
   383  	defer session.Close()
   384  	if err := session.Shell(); err != nil {
   385  		t.Fatalf("Unable to execute command: %v", err)
   386  	}
   387  	err = session.Wait()
   388  	if err != nil {
   389  		t.Fatalf("expected nil but got: %v", err)
   390  	}
   391  }
   392  
   393  type exitStatusMsg struct {
   394  	Status uint32
   395  }
   396  
   397  type exitSignalMsg struct {
   398  	Signal     string
   399  	CoreDumped bool
   400  	Errmsg     string
   401  	Lang       string
   402  }
   403  
   404  func handleTerminalRequests(in <-chan *Request) {
   405  	for req := range in {
   406  		ok := false
   407  		switch req.Type {
   408  		case "shell":
   409  			ok = true
   410  			if len(req.Payload) > 0 {
   411  				// We don't accept any commands, only the default shell.
   412  				ok = false
   413  			}
   414  		case "env":
   415  			ok = true
   416  		}
   417  		req.Reply(ok, nil)
   418  	}
   419  }
   420  
   421  func newServerShell(ch Channel, in <-chan *Request, prompt string) *terminal.Terminal {
   422  	term := terminal.NewTerminal(ch, prompt)
   423  	go handleTerminalRequests(in)
   424  	return term
   425  }
   426  
   427  func exitStatusZeroHandler(ch Channel, in <-chan *Request, t *testing.T) {
   428  	defer ch.Close()
   429  	// this string is returned to stdout
   430  	shell := newServerShell(ch, in, "> ")
   431  	readLine(shell, t)
   432  	sendStatus(0, ch, t)
   433  }
   434  
   435  func exitStatusNonZeroHandler(ch Channel, in <-chan *Request, t *testing.T) {
   436  	defer ch.Close()
   437  	shell := newServerShell(ch, in, "> ")
   438  	readLine(shell, t)
   439  	sendStatus(15, ch, t)
   440  }
   441  
   442  func exitSignalAndStatusHandler(ch Channel, in <-chan *Request, t *testing.T) {
   443  	defer ch.Close()
   444  	shell := newServerShell(ch, in, "> ")
   445  	readLine(shell, t)
   446  	sendStatus(15, ch, t)
   447  	sendSignal("TERM", ch, t)
   448  }
   449  
   450  func exitSignalHandler(ch Channel, in <-chan *Request, t *testing.T) {
   451  	defer ch.Close()
   452  	shell := newServerShell(ch, in, "> ")
   453  	readLine(shell, t)
   454  	sendSignal("TERM", ch, t)
   455  }
   456  
   457  func exitSignalUnknownHandler(ch Channel, in <-chan *Request, t *testing.T) {
   458  	defer ch.Close()
   459  	shell := newServerShell(ch, in, "> ")
   460  	readLine(shell, t)
   461  	sendSignal("SYS", ch, t)
   462  }
   463  
   464  func exitWithoutSignalOrStatus(ch Channel, in <-chan *Request, t *testing.T) {
   465  	defer ch.Close()
   466  	shell := newServerShell(ch, in, "> ")
   467  	readLine(shell, t)
   468  }
   469  
   470  func shellHandler(ch Channel, in <-chan *Request, t *testing.T) {
   471  	defer ch.Close()
   472  	// this string is returned to stdout
   473  	shell := newServerShell(ch, in, "golang")
   474  	readLine(shell, t)
   475  	sendStatus(0, ch, t)
   476  }
   477  
   478  // Ignores the command, writes fixed strings to stderr and stdout.
   479  // Strings are "this-is-stdout." and "this-is-stderr.".
   480  func fixedOutputHandler(ch Channel, in <-chan *Request, t *testing.T) {
   481  	defer ch.Close()
   482  	_, err := ch.Read(nil)
   483  
   484  	req, ok := <-in
   485  	if !ok {
   486  		t.Fatalf("error: expected channel request, got: %#v", err)
   487  		return
   488  	}
   489  
   490  	// ignore request, always send some text
   491  	req.Reply(true, nil)
   492  
   493  	_, err = io.WriteString(ch, "this-is-stdout.")
   494  	if err != nil {
   495  		t.Fatalf("error writing on server: %v", err)
   496  	}
   497  	_, err = io.WriteString(ch.Stderr(), "this-is-stderr.")
   498  	if err != nil {
   499  		t.Fatalf("error writing on server: %v", err)
   500  	}
   501  	sendStatus(0, ch, t)
   502  }
   503  
   504  func readLine(shell *terminal.Terminal, t *testing.T) {
   505  	if _, err := shell.ReadLine(); err != nil && err != io.EOF {
   506  		t.Errorf("unable to read line: %v", err)
   507  	}
   508  }
   509  
   510  func sendStatus(status uint32, ch Channel, t *testing.T) {
   511  	msg := exitStatusMsg{
   512  		Status: status,
   513  	}
   514  	if _, err := ch.SendRequest("exit-status", false, Marshal(&msg)); err != nil {
   515  		t.Errorf("unable to send status: %v", err)
   516  	}
   517  }
   518  
   519  func sendSignal(signal string, ch Channel, t *testing.T) {
   520  	sig := exitSignalMsg{
   521  		Signal:     signal,
   522  		CoreDumped: false,
   523  		Errmsg:     "Process terminated",
   524  		Lang:       "en-GB-oed",
   525  	}
   526  	if _, err := ch.SendRequest("exit-signal", false, Marshal(&sig)); err != nil {
   527  		t.Errorf("unable to send signal: %v", err)
   528  	}
   529  }
   530  
   531  func discardHandler(ch Channel, t *testing.T) {
   532  	defer ch.Close()
   533  	io.Copy(ioutil.Discard, ch)
   534  }
   535  
   536  func echoHandler(ch Channel, in <-chan *Request, t *testing.T) {
   537  	defer ch.Close()
   538  	if n, err := copyNRandomly("echohandler", ch, ch, windowTestBytes); err != nil {
   539  		t.Errorf("short write, wrote %d, expected %d: %v ", n, windowTestBytes, err)
   540  	}
   541  }
   542  
   543  // copyNRandomly copies n bytes from src to dst. It uses a variable, and random,
   544  // buffer size to exercise more code paths.
   545  func copyNRandomly(title string, dst io.Writer, src io.Reader, n int) (int, error) {
   546  	var (
   547  		buf       = make([]byte, 32*1024)
   548  		written   int
   549  		remaining = n
   550  	)
   551  	for remaining > 0 {
   552  		l := rand.Intn(1 << 15)
   553  		if remaining < l {
   554  			l = remaining
   555  		}
   556  		nr, er := src.Read(buf[:l])
   557  		nw, ew := dst.Write(buf[:nr])
   558  		remaining -= nw
   559  		written += nw
   560  		if ew != nil {
   561  			return written, ew
   562  		}
   563  		if nr != nw {
   564  			return written, io.ErrShortWrite
   565  		}
   566  		if er != nil && er != io.EOF {
   567  			return written, er
   568  		}
   569  	}
   570  	return written, nil
   571  }
   572  
   573  func channelKeepaliveSender(ch Channel, in <-chan *Request, t *testing.T) {
   574  	defer ch.Close()
   575  	shell := newServerShell(ch, in, "> ")
   576  	readLine(shell, t)
   577  	if _, err := ch.SendRequest("keepalive@openssh.com", true, nil); err != nil {
   578  		t.Errorf("unable to send channel keepalive request: %v", err)
   579  	}
   580  	sendStatus(0, ch, t)
   581  }
   582  
   583  func TestClientWriteEOF(t *testing.T) {
   584  	conn := dial(simpleEchoHandler, t)
   585  	defer conn.Close()
   586  
   587  	session, err := conn.NewSession()
   588  	if err != nil {
   589  		t.Fatal(err)
   590  	}
   591  	defer session.Close()
   592  	stdin, err := session.StdinPipe()
   593  	if err != nil {
   594  		t.Fatalf("StdinPipe failed: %v", err)
   595  	}
   596  	stdout, err := session.StdoutPipe()
   597  	if err != nil {
   598  		t.Fatalf("StdoutPipe failed: %v", err)
   599  	}
   600  
   601  	data := []byte(`0000`)
   602  	_, err = stdin.Write(data)
   603  	if err != nil {
   604  		t.Fatalf("Write failed: %v", err)
   605  	}
   606  	stdin.Close()
   607  
   608  	res, err := ioutil.ReadAll(stdout)
   609  	if err != nil {
   610  		t.Fatalf("Read failed: %v", err)
   611  	}
   612  
   613  	if !bytes.Equal(data, res) {
   614  		t.Fatalf("Read differed from write, wrote: %v, read: %v", data, res)
   615  	}
   616  }
   617  
   618  func simpleEchoHandler(ch Channel, in <-chan *Request, t *testing.T) {
   619  	defer ch.Close()
   620  	data, err := ioutil.ReadAll(ch)
   621  	if err != nil {
   622  		t.Errorf("handler read error: %v", err)
   623  	}
   624  	_, err = ch.Write(data)
   625  	if err != nil {
   626  		t.Errorf("handler write error: %v", err)
   627  	}
   628  }
   629  
   630  func TestSessionID(t *testing.T) {
   631  	c1, c2, err := netPipe()
   632  	if err != nil {
   633  		t.Fatalf("netPipe: %v", err)
   634  	}
   635  	defer c1.Close()
   636  	defer c2.Close()
   637  
   638  	serverID := make(chan []byte, 1)
   639  	clientID := make(chan []byte, 1)
   640  
   641  	serverConf := &ServerConfig{
   642  		NoClientAuth: true,
   643  	}
   644  	serverConf.AddHostKey(testSigners["ecdsa"])
   645  	clientConf := &ClientConfig{
   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  }