Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 

771 rader
19 KiB

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