| // Copyright 2023 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package test | |
| import ( | |
| "net" | |
| "golang.org/x/crypto/ssh" | |
| ) | |
| type exitStatusMsg struct { | |
| Status uint32 | |
| } | |
| // goTestServer is a test Go SSH server that accepts public key and certificate | |
| // authentication and replies with a 0 exit status to any exec request without | |
| // running any commands. | |
| type goTestServer struct { | |
| listener net.Listener | |
| config *ssh.ServerConfig | |
| done <-chan struct{} | |
| } | |
| func newTestServer(config *ssh.ServerConfig) (*goTestServer, error) { | |
| server := &goTestServer{ | |
| config: config, | |
| } | |
| listener, err := net.Listen("tcp", "127.0.0.1:") | |
| if err != nil { | |
| return nil, err | |
| } | |
| server.listener = listener | |
| done := make(chan struct{}, 1) | |
| server.done = done | |
| go server.acceptConnections(done) | |
| return server, nil | |
| } | |
| func (s *goTestServer) port() (string, error) { | |
| _, port, err := net.SplitHostPort(s.listener.Addr().String()) | |
| return port, err | |
| } | |
| func (s *goTestServer) acceptConnections(done chan<- struct{}) { | |
| defer close(done) | |
| for { | |
| c, err := s.listener.Accept() | |
| if err != nil { | |
| return | |
| } | |
| _, chans, reqs, err := ssh.NewServerConn(c, s.config) | |
| if err != nil { | |
| return | |
| } | |
| go ssh.DiscardRequests(reqs) | |
| defer c.Close() | |
| for newChannel := range chans { | |
| if newChannel.ChannelType() != "session" { | |
| newChannel.Reject(ssh.UnknownChannelType, "unknown channel type") | |
| continue | |
| } | |
| channel, requests, err := newChannel.Accept() | |
| if err != nil { | |
| continue | |
| } | |
| go func(in <-chan *ssh.Request) { | |
| for req := range in { | |
| ok := false | |
| switch req.Type { | |
| case "exec": | |
| ok = true | |
| go func() { | |
| channel.SendRequest("exit-status", false, ssh.Marshal(&exitStatusMsg{Status: 0})) | |
| channel.Close() | |
| }() | |
| } | |
| if req.WantReply { | |
| req.Reply(ok, nil) | |
| } | |
| } | |
| }(requests) | |
| } | |
| } | |
| } | |
| func (s *goTestServer) Close() error { | |
| err := s.listener.Close() | |
| // wait for the accept loop to exit | |
| <-s.done | |
| return err | |
| } |