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