// 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 (
	
	
	
	
	
	
	
)

// Client implements a traditional SSH client that supports shells,
// subprocesses, TCP port/streamlocal forwarding and tunneled dialing.
type Client struct {
	Conn

	handleForwardsOnce sync.Once // guards calling (*Client).handleForwards

	forwards        forwardList // forwarded tcpip connections from the remote side
	mu              sync.Mutex
	channelHandlers map[string]chan NewChannel
}

// HandleChannelOpen returns a channel on which NewChannel requests
// for the given type are sent. If the type already is being handled,
// nil is returned. The channel is closed when the connection is closed.
func ( *Client) ( string) <-chan NewChannel {
	.mu.Lock()
	defer .mu.Unlock()
	if .channelHandlers == nil {
		// The SSH channel has been closed.
		 := make(chan NewChannel)
		close()
		return 
	}

	 := .channelHandlers[]
	if  != nil {
		return nil
	}

	 = make(chan NewChannel, chanSize)
	.channelHandlers[] = 
	return 
}

// NewClient creates a Client on top of the given connection.
func ( Conn,  <-chan NewChannel,  <-chan *Request) *Client {
	 := &Client{
		Conn:            ,
		channelHandlers: make(map[string]chan NewChannel, 1),
	}

	go .handleGlobalRequests()
	go .handleChannelOpens()
	go func() {
		.Wait()
		.forwards.closeAll()
	}()
	return 
}

// NewClientConn establishes an authenticated SSH connection using c
// as the underlying transport.  The Request and NewChannel channels
// must be serviced or the connection will hang.
func ( net.Conn,  string,  *ClientConfig) (Conn, <-chan NewChannel, <-chan *Request, error) {
	 := *
	.SetDefaults()
	if .HostKeyCallback == nil {
		.Close()
		return nil, nil, nil, errors.New("ssh: must specify HostKeyCallback")
	}

	 := &connection{
		sshConn: sshConn{conn: , user: .User},
	}

	if  := .clientHandshake(, &);  != nil {
		.Close()
		return nil, nil, nil, fmt.Errorf("ssh: handshake failed: %v", )
	}
	.mux = newMux(.transport)
	return , .mux.incomingChannels, .mux.incomingRequests, nil
}

// clientHandshake performs the client side key exchange. See RFC 4253 Section
// 7.
func ( *connection) ( string,  *ClientConfig) error {
	if .ClientVersion != "" {
		.clientVersion = []byte(.ClientVersion)
	} else {
		.clientVersion = []byte(packageVersion)
	}
	var  error
	.serverVersion,  = exchangeVersions(.sshConn.conn, .clientVersion)
	if  != nil {
		return 
	}

	.transport = newClientTransport(
		newTransport(.sshConn.conn, .Rand, true /* is client */),
		.clientVersion, .serverVersion, , , .sshConn.RemoteAddr())
	if  := .transport.waitSession();  != nil {
		return 
	}

	.sessionID = .transport.getSessionID()
	return .clientAuthenticate()
}

// verifyHostKeySignature verifies the host key obtained in the key exchange.
// algo is the negotiated algorithm, and may be a certificate type.
func verifyHostKeySignature( PublicKey,  string,  *kexResult) error {
	, ,  := parseSignatureBody(.Signature)
	if len() > 0 || ! {
		return errors.New("ssh: signature parse error")
	}

	if  := underlyingAlgo(); .Format !=  {
		return fmt.Errorf("ssh: invalid signature algorithm %q, expected %q", .Format, )
	}

	return .Verify(.H, )
}

// NewSession opens a new Session for this client. (A session is a remote
// execution of a program.)
func ( *Client) () (*Session, error) {
	, ,  := .OpenChannel("session", nil)
	if  != nil {
		return nil, 
	}
	return newSession(, )
}

func ( *Client) ( <-chan *Request) {
	for  := range  {
		// This handles keepalive messages and matches
		// the behaviour of OpenSSH.
		.Reply(false, nil)
	}
}

// handleChannelOpens channel open messages from the remote side.
func ( *Client) ( <-chan NewChannel) {
	for  := range  {
		.mu.Lock()
		 := .channelHandlers[.ChannelType()]
		.mu.Unlock()

		if  != nil {
			 <- 
		} else {
			.Reject(UnknownChannelType, fmt.Sprintf("unknown channel type: %v", .ChannelType()))
		}
	}

	.mu.Lock()
	for ,  := range .channelHandlers {
		close()
	}
	.channelHandlers = nil
	.mu.Unlock()
}

// Dial starts a client connection to the given SSH server. It is a
// convenience function that connects to the given network address,
// initiates the SSH handshake, and then sets up a Client.  For access
// to incoming channels and requests, use net.Dial with NewClientConn
// instead.
func (,  string,  *ClientConfig) (*Client, error) {
	,  := net.DialTimeout(, , .Timeout)
	if  != nil {
		return nil, 
	}
	, , ,  := NewClientConn(, , )
	if  != nil {
		return nil, 
	}
	return NewClient(, , ), nil
}

// HostKeyCallback is the function type used for verifying server
// keys.  A HostKeyCallback must return nil if the host key is OK, or
// an error to reject it. It receives the hostname as passed to Dial
// or NewClientConn. The remote address is the RemoteAddr of the
// net.Conn underlying the SSH connection.
type HostKeyCallback func(hostname string, remote net.Addr, key PublicKey) error

// BannerCallback is the function type used for treat the banner sent by
// the server. A BannerCallback receives the message sent by the remote server.
type BannerCallback func(message string) error

// A ClientConfig structure is used to configure a Client. It must not be
// modified after having been passed to an SSH function.
type ClientConfig struct {
	// Config contains configuration that is shared between clients and
	// servers.
	Config

	// User contains the username to authenticate as.
	User string

	// Auth contains possible authentication methods to use with the
	// server. Only the first instance of a particular RFC 4252 method will
	// be used during authentication.
	Auth []AuthMethod

	// HostKeyCallback is called during the cryptographic
	// handshake to validate the server's host key. The client
	// configuration must supply this callback for the connection
	// to succeed. The functions InsecureIgnoreHostKey or
	// FixedHostKey can be used for simplistic host key checks.
	HostKeyCallback HostKeyCallback

	// BannerCallback is called during the SSH dance to display a custom
	// server's message. The client configuration can supply this callback to
	// handle it as wished. The function BannerDisplayStderr can be used for
	// simplistic display on Stderr.
	BannerCallback BannerCallback

	// ClientVersion contains the version identification string that will
	// be used for the connection. If empty, a reasonable default is used.
	ClientVersion string

	// HostKeyAlgorithms lists the public key algorithms that the client will
	// accept from the server for host key authentication, in order of
	// preference. If empty, a reasonable default is used. Any
	// string returned from a PublicKey.Type method may be used, or
	// any of the CertAlgo and KeyAlgo constants.
	HostKeyAlgorithms []string

	// Timeout is the maximum amount of time for the TCP connection to establish.
	//
	// A Timeout of zero means no timeout.
	Timeout time.Duration
}

// InsecureIgnoreHostKey returns a function that can be used for
// ClientConfig.HostKeyCallback to accept any host key. It should
// not be used for production code.
func () HostKeyCallback {
	return func( string,  net.Addr,  PublicKey) error {
		return nil
	}
}

type fixedHostKey struct {
	key PublicKey
}

func ( *fixedHostKey) ( string,  net.Addr,  PublicKey) error {
	if .key == nil {
		return fmt.Errorf("ssh: required host key was nil")
	}
	if !bytes.Equal(.Marshal(), .key.Marshal()) {
		return fmt.Errorf("ssh: host key mismatch")
	}
	return nil
}

// FixedHostKey returns a function for use in
// ClientConfig.HostKeyCallback to accept only a specific host key.
func ( PublicKey) HostKeyCallback {
	 := &fixedHostKey{}
	return .check
}

// BannerDisplayStderr returns a function that can be used for
// ClientConfig.BannerCallback to display banners on os.Stderr.
func () BannerCallback {
	return func( string) error {
		,  := os.Stderr.WriteString()

		return 
	}
}