// 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 } // goServer 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 }