woodpecker/vendor/code.google.com/p/go.crypto/ssh/server.go

693 lines
18 KiB
Go
Raw Normal View History

2015-05-22 18:37:40 +00:00
// Copyright 2011 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 ssh
import (
"bytes"
2015-09-30 01:21:17 +00:00
"crypto/rand"
"encoding/binary"
2015-05-22 18:37:40 +00:00
"errors"
"fmt"
"io"
"net"
2015-09-30 01:21:17 +00:00
"sync"
2015-05-22 18:37:40 +00:00
2015-09-30 01:21:17 +00:00
_ "crypto/sha1"
)
2015-05-22 18:37:40 +00:00
type ServerConfig struct {
hostKeys []Signer
2015-09-30 01:21:17 +00:00
// Rand provides the source of entropy for key exchange. If Rand is
// nil, the cryptographic random reader in package crypto/rand will
// be used.
Rand io.Reader
2015-05-22 18:37:40 +00:00
// NoClientAuth is true if clients are allowed to connect without
// authenticating.
NoClientAuth bool
2015-09-30 01:21:17 +00:00
// PasswordCallback, if non-nil, is called when a user attempts to
// authenticate using a password. It may be called concurrently from
// several goroutines.
PasswordCallback func(conn *ServerConn, user, password string) bool
2015-05-22 18:37:40 +00:00
// PublicKeyCallback, if non-nil, is called when a client attempts public
// key authentication. It must return true if the given public key is
2015-09-30 01:21:17 +00:00
// valid for the given user.
PublicKeyCallback func(conn *ServerConn, user, algo string, pubkey []byte) bool
2015-05-22 18:37:40 +00:00
// KeyboardInteractiveCallback, if non-nil, is called when
// keyboard-interactive authentication is selected (RFC
// 4256). The client object's Challenge function should be
// used to query the user. The callback may offer multiple
// Challenge rounds. To avoid information leaks, the client
// should be presented a challenge even if the user is
// unknown.
2015-09-30 01:21:17 +00:00
KeyboardInteractiveCallback func(conn *ServerConn, user string, client ClientKeyboardInteractive) bool
2015-05-22 18:37:40 +00:00
2015-09-30 01:21:17 +00:00
// Cryptographic-related configuration.
Crypto CryptoConfig
}
func (c *ServerConfig) rand() io.Reader {
if c.Rand == nil {
return rand.Reader
}
return c.Rand
2015-05-22 18:37:40 +00:00
}
// AddHostKey adds a private key as a host key. If an existing host
2015-09-30 01:21:17 +00:00
// key exists with the same algorithm, it is overwritten.
2015-05-22 18:37:40 +00:00
func (s *ServerConfig) AddHostKey(key Signer) {
for i, k := range s.hostKeys {
2015-09-30 01:21:17 +00:00
if k.PublicKey().PublicKeyAlgo() == key.PublicKey().PublicKeyAlgo() {
2015-05-22 18:37:40 +00:00
s.hostKeys[i] = key
return
}
}
s.hostKeys = append(s.hostKeys, key)
}
2015-09-30 01:21:17 +00:00
// SetRSAPrivateKey sets the private key for a Server. A Server must have a
// private key configured in order to accept connections. The private key must
// be in the form of a PEM encoded, PKCS#1, RSA private key. The file "id_rsa"
// typically contains such a key.
func (s *ServerConfig) SetRSAPrivateKey(pemBytes []byte) error {
priv, err := ParsePrivateKey(pemBytes)
if err != nil {
return err
}
s.AddHostKey(priv)
return nil
}
2015-05-22 18:37:40 +00:00
// cachedPubKey contains the results of querying whether a public key is
2015-09-30 01:21:17 +00:00
// acceptable for a user. The cache only applies to a single ServerConn.
2015-05-22 18:37:40 +00:00
type cachedPubKey struct {
2015-09-30 01:21:17 +00:00
user, algo string
pubKey []byte
result bool
2015-05-22 18:37:40 +00:00
}
const maxCachedPubKeys = 16
2015-09-30 01:21:17 +00:00
// A ServerConn represents an incoming connection.
type ServerConn struct {
transport *transport
config *ServerConfig
2015-05-22 18:37:40 +00:00
2015-09-30 01:21:17 +00:00
channels map[uint32]*serverChan
nextChanId uint32
2015-05-22 18:37:40 +00:00
2015-09-30 01:21:17 +00:00
// lock protects err and channels.
lock sync.Mutex
err error
2015-05-22 18:37:40 +00:00
2015-09-30 01:21:17 +00:00
// cachedPubKeys contains the cache results of tests for public keys.
// Since SSH clients will query whether a public key is acceptable
// before attempting to authenticate with it, we end up with duplicate
// queries for public key validity.
cachedPubKeys []cachedPubKey
// User holds the successfully authenticated user name.
// It is empty if no authentication is used. It is populated before
// any authentication callback is called and not assigned to after that.
User string
2015-05-22 18:37:40 +00:00
2015-09-30 01:21:17 +00:00
// ClientVersion is the client's version, populated after
// Handshake is called. It should not be modified.
ClientVersion []byte
// Our version.
serverVersion []byte
2015-05-22 18:37:40 +00:00
}
2015-09-30 01:21:17 +00:00
// Server returns a new SSH server connection
// using c as the underlying transport.
func Server(c net.Conn, config *ServerConfig) *ServerConn {
return &ServerConn{
transport: newTransport(c, config.rand(), false /* not client */),
channels: make(map[uint32]*serverChan),
config: config,
2015-05-22 18:37:40 +00:00
}
}
// signAndMarshal signs the data with the appropriate algorithm,
// and serializes the result in SSH wire format.
func signAndMarshal(k Signer, rand io.Reader, data []byte) ([]byte, error) {
sig, err := k.Sign(rand, data)
if err != nil {
return nil, err
}
2015-09-30 01:21:17 +00:00
return serializeSignature(k.PublicKey().PrivateKeyAlgo(), sig), nil
2015-05-22 18:37:40 +00:00
}
2015-09-30 01:21:17 +00:00
// Close closes the connection.
func (s *ServerConn) Close() error { return s.transport.Close() }
2015-05-22 18:37:40 +00:00
2015-09-30 01:21:17 +00:00
// LocalAddr returns the local network address.
func (c *ServerConn) LocalAddr() net.Addr { return c.transport.LocalAddr() }
// RemoteAddr returns the remote network address.
func (c *ServerConn) RemoteAddr() net.Addr { return c.transport.RemoteAddr() }
// Handshake performs an SSH transport and client authentication on the given ServerConn.
func (s *ServerConn) Handshake() error {
2015-05-22 18:37:40 +00:00
var err error
s.serverVersion = []byte(packageVersion)
2015-09-30 01:21:17 +00:00
s.ClientVersion, err = exchangeVersions(s.transport.Conn, s.serverVersion)
2015-05-22 18:37:40 +00:00
if err != nil {
2015-09-30 01:21:17 +00:00
return err
2015-05-22 18:37:40 +00:00
}
2015-09-30 01:21:17 +00:00
if err := s.clientInitHandshake(nil, nil); err != nil {
return err
2015-05-22 18:37:40 +00:00
}
var packet []byte
if packet, err = s.transport.readPacket(); err != nil {
2015-09-30 01:21:17 +00:00
return err
2015-05-22 18:37:40 +00:00
}
var serviceRequest serviceRequestMsg
2015-09-30 01:21:17 +00:00
if err := unmarshal(&serviceRequest, packet, msgServiceRequest); err != nil {
return err
2015-05-22 18:37:40 +00:00
}
if serviceRequest.Service != serviceUserAuth {
2015-09-30 01:21:17 +00:00
return errors.New("ssh: requested service '" + serviceRequest.Service + "' before authenticating")
2015-05-22 18:37:40 +00:00
}
serviceAccept := serviceAcceptMsg{
Service: serviceUserAuth,
}
2015-09-30 01:21:17 +00:00
if err := s.transport.writePacket(marshal(msgServiceAccept, serviceAccept)); err != nil {
return err
}
if err := s.authenticate(); err != nil {
return err
}
return err
}
func (s *ServerConn) clientInitHandshake(clientKexInit *kexInitMsg, clientKexInitPacket []byte) (err error) {
serverKexInit := kexInitMsg{
KexAlgos: s.config.Crypto.kexes(),
CiphersClientServer: s.config.Crypto.ciphers(),
CiphersServerClient: s.config.Crypto.ciphers(),
MACsClientServer: s.config.Crypto.macs(),
MACsServerClient: s.config.Crypto.macs(),
CompressionClientServer: supportedCompressions,
CompressionServerClient: supportedCompressions,
}
for _, k := range s.config.hostKeys {
serverKexInit.ServerHostKeyAlgos = append(
serverKexInit.ServerHostKeyAlgos, k.PublicKey().PublicKeyAlgo())
}
serverKexInitPacket := marshal(msgKexInit, serverKexInit)
if err = s.transport.writePacket(serverKexInitPacket); err != nil {
return
}
if clientKexInitPacket == nil {
clientKexInit = new(kexInitMsg)
if clientKexInitPacket, err = s.transport.readPacket(); err != nil {
return
}
if err = unmarshal(clientKexInit, clientKexInitPacket, msgKexInit); err != nil {
return
}
}
algs := findAgreedAlgorithms(clientKexInit, &serverKexInit)
if algs == nil {
return errors.New("ssh: no common algorithms")
}
if clientKexInit.FirstKexFollows && algs.kex != clientKexInit.KexAlgos[0] {
// The client sent a Kex message for the wrong algorithm,
// which we have to ignore.
if _, err = s.transport.readPacket(); err != nil {
return
}
}
var hostKey Signer
for _, k := range s.config.hostKeys {
if algs.hostKey == k.PublicKey().PublicKeyAlgo() {
hostKey = k
}
}
kex, ok := kexAlgoMap[algs.kex]
if !ok {
return fmt.Errorf("ssh: unexpected key exchange algorithm %v", algs.kex)
2015-05-22 18:37:40 +00:00
}
2015-09-30 01:21:17 +00:00
magics := handshakeMagics{
serverVersion: s.serverVersion,
clientVersion: s.ClientVersion,
serverKexInit: marshal(msgKexInit, serverKexInit),
clientKexInit: clientKexInitPacket,
}
result, err := kex.Server(s.transport, s.config.rand(), &magics, hostKey)
2015-05-22 18:37:40 +00:00
if err != nil {
2015-09-30 01:21:17 +00:00
return err
}
if err = s.transport.prepareKeyChange(algs, result); err != nil {
return err
}
if err = s.transport.writePacket([]byte{msgNewKeys}); err != nil {
return
}
if packet, err := s.transport.readPacket(); err != nil {
return err
} else if packet[0] != msgNewKeys {
return UnexpectedMessageError{msgNewKeys, packet[0]}
2015-05-22 18:37:40 +00:00
}
2015-09-30 01:21:17 +00:00
return
2015-05-22 18:37:40 +00:00
}
func isAcceptableAlgo(algo string) bool {
switch algo {
case KeyAlgoRSA, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01:
return true
}
return false
}
2015-09-30 01:21:17 +00:00
// testPubKey returns true if the given public key is acceptable for the user.
func (s *ServerConn) testPubKey(user, algo string, pubKey []byte) bool {
if s.config.PublicKeyCallback == nil || !isAcceptableAlgo(algo) {
return false
2015-05-22 18:37:40 +00:00
}
2015-09-30 01:21:17 +00:00
for _, c := range s.cachedPubKeys {
if c.user == user && c.algo == algo && bytes.Equal(c.pubKey, pubKey) {
return c.result
2015-05-22 18:37:40 +00:00
}
2015-09-30 01:21:17 +00:00
}
2015-05-22 18:37:40 +00:00
2015-09-30 01:21:17 +00:00
result := s.config.PublicKeyCallback(s, user, algo, pubKey)
if len(s.cachedPubKeys) < maxCachedPubKeys {
c := cachedPubKey{
user: user,
algo: algo,
pubKey: make([]byte, len(pubKey)),
result: result,
2015-05-22 18:37:40 +00:00
}
2015-09-30 01:21:17 +00:00
copy(c.pubKey, pubKey)
s.cachedPubKeys = append(s.cachedPubKeys, c)
2015-05-22 18:37:40 +00:00
}
2015-09-30 01:21:17 +00:00
return result
2015-05-22 18:37:40 +00:00
}
2015-09-30 01:21:17 +00:00
func (s *ServerConn) authenticate() error {
var userAuthReq userAuthRequestMsg
2015-05-22 18:37:40 +00:00
var err error
2015-09-30 01:21:17 +00:00
var packet []byte
2015-05-22 18:37:40 +00:00
userAuthLoop:
for {
2015-09-30 01:21:17 +00:00
if packet, err = s.transport.readPacket(); err != nil {
return err
}
if err = unmarshal(&userAuthReq, packet, msgUserAuthRequest); err != nil {
return err
2015-05-22 18:37:40 +00:00
}
if userAuthReq.Service != serviceSSH {
2015-09-30 01:21:17 +00:00
return errors.New("ssh: client attempted to negotiate for unknown service: " + userAuthReq.Service)
2015-05-22 18:37:40 +00:00
}
switch userAuthReq.Method {
case "none":
2015-09-30 01:21:17 +00:00
if s.config.NoClientAuth {
break userAuthLoop
2015-05-22 18:37:40 +00:00
}
case "password":
2015-09-30 01:21:17 +00:00
if s.config.PasswordCallback == nil {
2015-05-22 18:37:40 +00:00
break
}
payload := userAuthReq.Payload
if len(payload) < 1 || payload[0] != 0 {
2015-09-30 01:21:17 +00:00
return ParseError{msgUserAuthRequest}
2015-05-22 18:37:40 +00:00
}
payload = payload[1:]
password, payload, ok := parseString(payload)
if !ok || len(payload) > 0 {
2015-09-30 01:21:17 +00:00
return ParseError{msgUserAuthRequest}
2015-05-22 18:37:40 +00:00
}
2015-09-30 01:21:17 +00:00
s.User = userAuthReq.User
if s.config.PasswordCallback(s, userAuthReq.User, string(password)) {
break userAuthLoop
}
2015-05-22 18:37:40 +00:00
case "keyboard-interactive":
2015-09-30 01:21:17 +00:00
if s.config.KeyboardInteractiveCallback == nil {
2015-05-22 18:37:40 +00:00
break
}
2015-09-30 01:21:17 +00:00
s.User = userAuthReq.User
if s.config.KeyboardInteractiveCallback(s, s.User, &sshClientKeyboardInteractive{s}) {
break userAuthLoop
}
2015-05-22 18:37:40 +00:00
case "publickey":
2015-09-30 01:21:17 +00:00
if s.config.PublicKeyCallback == nil {
2015-05-22 18:37:40 +00:00
break
}
payload := userAuthReq.Payload
if len(payload) < 1 {
2015-09-30 01:21:17 +00:00
return ParseError{msgUserAuthRequest}
2015-05-22 18:37:40 +00:00
}
isQuery := payload[0] == 0
payload = payload[1:]
algoBytes, payload, ok := parseString(payload)
if !ok {
2015-09-30 01:21:17 +00:00
return ParseError{msgUserAuthRequest}
2015-05-22 18:37:40 +00:00
}
algo := string(algoBytes)
2015-09-30 01:21:17 +00:00
pubKey, payload, ok := parseString(payload)
2015-05-22 18:37:40 +00:00
if !ok {
2015-09-30 01:21:17 +00:00
return ParseError{msgUserAuthRequest}
2015-05-22 18:37:40 +00:00
}
if isQuery {
// The client can query if the given public key
// would be okay.
if len(payload) > 0 {
2015-09-30 01:21:17 +00:00
return ParseError{msgUserAuthRequest}
2015-05-22 18:37:40 +00:00
}
2015-09-30 01:21:17 +00:00
if s.testPubKey(userAuthReq.User, algo, pubKey) {
2015-05-22 18:37:40 +00:00
okMsg := userAuthPubKeyOkMsg{
Algo: algo,
2015-09-30 01:21:17 +00:00
PubKey: string(pubKey),
2015-05-22 18:37:40 +00:00
}
2015-09-30 01:21:17 +00:00
if err = s.transport.writePacket(marshal(msgUserAuthPubKeyOk, okMsg)); err != nil {
return err
2015-05-22 18:37:40 +00:00
}
continue userAuthLoop
}
} else {
sig, payload, ok := parseSignature(payload)
if !ok || len(payload) > 0 {
2015-09-30 01:21:17 +00:00
return ParseError{msgUserAuthRequest}
2015-05-22 18:37:40 +00:00
}
// Ensure the public key algo and signature algo
// are supported. Compare the private key
// algorithm name that corresponds to algo with
// sig.Format. This is usually the same, but
// for certs, the names differ.
2015-09-30 01:21:17 +00:00
if !isAcceptableAlgo(algo) || !isAcceptableAlgo(sig.Format) || pubAlgoToPrivAlgo(algo) != sig.Format {
2015-05-22 18:37:40 +00:00
break
}
2015-09-30 01:21:17 +00:00
signedData := buildDataSignedForAuth(s.transport.sessionID, userAuthReq, algoBytes, pubKey)
key, _, ok := ParsePublicKey(pubKey)
if !ok {
return ParseError{msgUserAuthRequest}
2015-05-22 18:37:40 +00:00
}
2015-09-30 01:21:17 +00:00
if !key.Verify(signedData, sig.Blob) {
return ParseError{msgUserAuthRequest}
}
// TODO(jmpittman): Implement full validation for certificates.
s.User = userAuthReq.User
if s.testPubKey(userAuthReq.User, algo, pubKey) {
break userAuthLoop
}
2015-05-22 18:37:40 +00:00
}
}
var failureMsg userAuthFailureMsg
2015-09-30 01:21:17 +00:00
if s.config.PasswordCallback != nil {
2015-05-22 18:37:40 +00:00
failureMsg.Methods = append(failureMsg.Methods, "password")
}
2015-09-30 01:21:17 +00:00
if s.config.PublicKeyCallback != nil {
2015-05-22 18:37:40 +00:00
failureMsg.Methods = append(failureMsg.Methods, "publickey")
}
2015-09-30 01:21:17 +00:00
if s.config.KeyboardInteractiveCallback != nil {
2015-05-22 18:37:40 +00:00
failureMsg.Methods = append(failureMsg.Methods, "keyboard-interactive")
}
if len(failureMsg.Methods) == 0 {
2015-09-30 01:21:17 +00:00
return errors.New("ssh: no authentication methods configured but NoClientAuth is also false")
2015-05-22 18:37:40 +00:00
}
2015-09-30 01:21:17 +00:00
if err = s.transport.writePacket(marshal(msgUserAuthFailure, failureMsg)); err != nil {
return err
2015-05-22 18:37:40 +00:00
}
}
2015-09-30 01:21:17 +00:00
packet = []byte{msgUserAuthSuccess}
if err = s.transport.writePacket(packet); err != nil {
return err
2015-05-22 18:37:40 +00:00
}
2015-09-30 01:21:17 +00:00
return nil
2015-05-22 18:37:40 +00:00
}
// sshClientKeyboardInteractive implements a ClientKeyboardInteractive by
// asking the client on the other side of a ServerConn.
type sshClientKeyboardInteractive struct {
2015-09-30 01:21:17 +00:00
*ServerConn
2015-05-22 18:37:40 +00:00
}
func (c *sshClientKeyboardInteractive) Challenge(user, instruction string, questions []string, echos []bool) (answers []string, err error) {
if len(questions) != len(echos) {
return nil, errors.New("ssh: echos and questions must have equal length")
}
var prompts []byte
for i := range questions {
prompts = appendString(prompts, questions[i])
prompts = appendBool(prompts, echos[i])
}
2015-09-30 01:21:17 +00:00
if err := c.transport.writePacket(marshal(msgUserAuthInfoRequest, userAuthInfoRequestMsg{
2015-05-22 18:37:40 +00:00
Instruction: instruction,
NumPrompts: uint32(len(questions)),
Prompts: prompts,
})); err != nil {
return nil, err
}
packet, err := c.transport.readPacket()
if err != nil {
return nil, err
}
if packet[0] != msgUserAuthInfoResponse {
2015-09-30 01:21:17 +00:00
return nil, UnexpectedMessageError{msgUserAuthInfoResponse, packet[0]}
2015-05-22 18:37:40 +00:00
}
packet = packet[1:]
n, packet, ok := parseUint32(packet)
if !ok || int(n) != len(questions) {
2015-09-30 01:21:17 +00:00
return nil, &ParseError{msgUserAuthInfoResponse}
2015-05-22 18:37:40 +00:00
}
for i := uint32(0); i < n; i++ {
ans, rest, ok := parseString(packet)
if !ok {
2015-09-30 01:21:17 +00:00
return nil, &ParseError{msgUserAuthInfoResponse}
2015-05-22 18:37:40 +00:00
}
answers = append(answers, string(ans))
packet = rest
}
if len(packet) != 0 {
return nil, errors.New("ssh: junk at end of message")
}
return answers, nil
}
2015-09-30 01:21:17 +00:00
const defaultWindowSize = 32768
// Accept reads and processes messages on a ServerConn. It must be called
// in order to demultiplex messages to any resulting Channels.
func (s *ServerConn) Accept() (Channel, error) {
// TODO(dfc) s.lock is not held here so visibility of s.err is not guaranteed.
if s.err != nil {
return nil, s.err
}
for {
packet, err := s.transport.readPacket()
if err != nil {
s.lock.Lock()
s.err = err
s.lock.Unlock()
// TODO(dfc) s.lock protects s.channels but isn't being held here.
for _, c := range s.channels {
c.setDead()
c.handleData(nil)
}
return nil, err
}
switch packet[0] {
case msgChannelData:
if len(packet) < 9 {
// malformed data packet
return nil, ParseError{msgChannelData}
}
remoteId := binary.BigEndian.Uint32(packet[1:5])
s.lock.Lock()
c, ok := s.channels[remoteId]
if !ok {
s.lock.Unlock()
continue
}
if length := binary.BigEndian.Uint32(packet[5:9]); length > 0 {
packet = packet[9:]
c.handleData(packet[:length])
}
s.lock.Unlock()
default:
decoded, err := decode(packet)
if err != nil {
return nil, err
}
switch msg := decoded.(type) {
case *channelOpenMsg:
if msg.MaxPacketSize < minPacketLength || msg.MaxPacketSize > 1<<31 {
return nil, errors.New("ssh: invalid MaxPacketSize from peer")
}
c := &serverChan{
channel: channel{
packetConn: s.transport,
remoteId: msg.PeersId,
remoteWin: window{Cond: newCond()},
maxPacket: msg.MaxPacketSize,
},
chanType: msg.ChanType,
extraData: msg.TypeSpecificData,
myWindow: defaultWindowSize,
serverConn: s,
cond: newCond(),
pendingData: make([]byte, defaultWindowSize),
}
c.remoteWin.add(msg.PeersWindow)
s.lock.Lock()
c.localId = s.nextChanId
s.nextChanId++
s.channels[c.localId] = c
s.lock.Unlock()
return c, nil
case *channelRequestMsg:
s.lock.Lock()
c, ok := s.channels[msg.PeersId]
if !ok {
s.lock.Unlock()
continue
}
c.handlePacket(msg)
s.lock.Unlock()
case *windowAdjustMsg:
s.lock.Lock()
c, ok := s.channels[msg.PeersId]
if !ok {
s.lock.Unlock()
continue
}
c.handlePacket(msg)
s.lock.Unlock()
case *channelEOFMsg:
s.lock.Lock()
c, ok := s.channels[msg.PeersId]
if !ok {
s.lock.Unlock()
continue
}
c.handlePacket(msg)
s.lock.Unlock()
case *channelCloseMsg:
s.lock.Lock()
c, ok := s.channels[msg.PeersId]
if !ok {
s.lock.Unlock()
continue
}
c.handlePacket(msg)
s.lock.Unlock()
case *globalRequestMsg:
if msg.WantReply {
if err := s.transport.writePacket([]byte{msgRequestFailure}); err != nil {
return nil, err
}
}
case *kexInitMsg:
s.lock.Lock()
if err := s.clientInitHandshake(msg, packet); err != nil {
s.lock.Unlock()
return nil, err
}
s.lock.Unlock()
case *disconnectMsg:
return nil, io.EOF
default:
// Unknown message. Ignore.
}
}
}
panic("unreachable")
}
// A Listener implements a network listener (net.Listener) for SSH connections.
type Listener struct {
listener net.Listener
config *ServerConfig
}
// Addr returns the listener's network address.
func (l *Listener) Addr() net.Addr {
return l.listener.Addr()
}
// Close closes the listener.
func (l *Listener) Close() error {
return l.listener.Close()
}
// Accept waits for and returns the next incoming SSH connection.
// The receiver should call Handshake() in another goroutine
// to avoid blocking the accepter.
func (l *Listener) Accept() (*ServerConn, error) {
c, err := l.listener.Accept()
if err != nil {
return nil, err
}
return Server(c, l.config), nil
}
// Listen creates an SSH listener accepting connections on
// the given network address using net.Listen.
func Listen(network, addr string, config *ServerConfig) (*Listener, error) {
l, err := net.Listen(network, addr)
if err != nil {
return nil, err
}
return &Listener{
l,
config,
}, nil
}