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 }