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

// Listen requests the remote peer open a listening socket on
// addr. Incoming connections will be available by calling Accept on
// the returned net.Listener. The listener must be serviced, or the
// SSH connection may hang.
// N must be "tcp", "tcp4", "tcp6", or "unix".
//
// If the address is a hostname, it is sent to the remote peer as-is, without
// being resolved locally, and the Listener Addr method will return a zero IP.
func ( *Client) (,  string) (net.Listener, error) {
	switch  {
	case "tcp", "tcp4", "tcp6":
		, ,  := net.SplitHostPort()
		if  != nil {
			return nil, 
		}
		,  := strconv.ParseInt(, 10, 32)
		if  != nil {
			return nil, 
		}
		return .listenTCPInternal(, int())
	case "unix":
		return .ListenUnix()
	default:
		return nil, fmt.Errorf("ssh: unsupported protocol: %s", )
	}
}

// Automatic port allocation is broken with OpenSSH before 6.0. See
// also https://bugzilla.mindrot.org/show_bug.cgi?id=2017.  In
// particular, OpenSSH 5.9 sends a channelOpenMsg with port number 0,
// rather than the actual port number. This means you can never open
// two different listeners with auto allocated ports. We work around
// this by trying explicit ports until we succeed.

const openSSHPrefix = "OpenSSH_"

var portRandomizer = rand.New(rand.NewSource(time.Now().UnixNano()))

// isBrokenOpenSSHVersion returns true if the given version string
// specifies a version of OpenSSH that is known to have a bug in port
// forwarding.
func isBrokenOpenSSHVersion( string) bool {
	 := strings.Index(, openSSHPrefix)
	if  < 0 {
		return false
	}
	 += len(openSSHPrefix)
	 := 
	for ;  < len(); ++ {
		if [] < '0' || [] > '9' {
			break
		}
	}
	,  := strconv.Atoi([:])
	return  < 6
}

// autoPortListenWorkaround simulates automatic port allocation by
// trying random ports repeatedly.
func ( *Client) ( *net.TCPAddr) (net.Listener, error) {
	var  net.Listener
	var  error
	const  = 10
	for  := 0;  < ; ++ {
		 := *
		.Port = 1024 + portRandomizer.Intn(60000)
		,  = .ListenTCP(&)
		if  == nil {
			.Port = .Port
			return , 
		}
	}
	return nil, fmt.Errorf("ssh: listen on random port failed after %d tries: %v", , )
}

// RFC 4254 7.1
type channelForwardMsg struct {
	addr  string
	rport uint32
}

// handleForwards starts goroutines handling forwarded connections.
// It's called on first use by (*Client).ListenTCP to not launch
// goroutines until needed.
func ( *Client) () {
	go .forwards.handleChannels(.HandleChannelOpen("forwarded-tcpip"))
	go .forwards.handleChannels(.HandleChannelOpen("forwarded-streamlocal@openssh.com"))
}

// ListenTCP requests the remote peer open a listening socket
// on laddr. Incoming connections will be available by calling
// Accept on the returned net.Listener.
//
// ListenTCP accepts an IP address, to provide a hostname use [Client.Listen]
// with "tcp", "tcp4", or "tcp6" network instead.
func ( *Client) ( *net.TCPAddr) (net.Listener, error) {
	.handleForwardsOnce.Do(.handleForwards)
	if .Port == 0 && isBrokenOpenSSHVersion(string(.ServerVersion())) {
		return .autoPortListenWorkaround()
	}

	return .listenTCPInternal(.IP.String(), .Port)
}

func ( *Client) ( string,  int) (net.Listener, error) {
	.handleForwardsOnce.Do(.handleForwards)

	 := channelForwardMsg{
		,
		uint32(),
	}
	// send message
	, ,  := .SendRequest("tcpip-forward", true, Marshal(&))
	if  != nil {
		return nil, 
	}
	if ! {
		return nil, errors.New("ssh: tcpip-forward request denied by peer")
	}

	// If the original port was 0, then the remote side will
	// supply a real port number in the response.
	if  == 0 {
		var  struct {
			 uint32
		}
		if  := Unmarshal(, &);  != nil {
			return nil, 
		}
		 = int(.)
	}
	// Construct a local address placeholder for the remote listener. If the
	// original host is an IP address, preserve it so that Listener.Addr()
	// reports the same IP. If the host is a hostname or cannot be parsed as an
	// IP, fall back to IPv4zero. The port field is always set, even if the
	// original port was 0, because in that case the remote server will assign
	// one, allowing callers to determine which port was selected.
	 := net.IPv4zero
	if ,  := netip.ParseAddr();  == nil {
		 = net.IP(.AsSlice())
	}
	 := &net.TCPAddr{
		IP:   ,
		Port: ,
	}
	 := net.JoinHostPort(, strconv.FormatInt(int64(), 10))
	 := .forwards.add("tcp", )

	return &tcpListener{, , , }, nil
}

// forwardList stores a mapping between remote
// forward requests and the tcpListeners.
type forwardList struct {
	sync.Mutex
	entries []forwardEntry
}

// forwardEntry represents an established mapping of a laddr on a
// remote ssh server to a channel connected to a tcpListener.
type forwardEntry struct {
	addr    string // host:port or socket path
	network string // tcp or unix
	c       chan forward
}

// forward represents an incoming forwarded tcpip connection. The
// arguments to add/remove/lookup should be address as specified in
// the original forward-request.
type forward struct {
	newCh NewChannel // the ssh client channel underlying this forward
	raddr net.Addr   // the raddr of the incoming connection
}

func ( *forwardList) (,  string) chan forward {
	.Lock()
	defer .Unlock()
	 := forwardEntry{
		addr:    ,
		network: ,
		c:       make(chan forward, 1),
	}
	.entries = append(.entries, )
	return .c
}

// See RFC 4254, section 7.2
type forwardedTCPPayload struct {
	Addr       string
	Port       uint32
	OriginAddr string
	OriginPort uint32
}

// parseTCPAddr parses the originating address from the remote into a *net.TCPAddr.
func parseTCPAddr( string,  uint32) (*net.TCPAddr, error) {
	if  == 0 ||  > 65535 {
		return nil, fmt.Errorf("ssh: port number out of range: %d", )
	}
	,  := netip.ParseAddr()
	if  != nil {
		return nil, fmt.Errorf("ssh: cannot parse IP address %q", )
	}
	return &net.TCPAddr{IP: net.IP(.AsSlice()), Port: int()}, nil
}

func ( *forwardList) ( <-chan NewChannel) {
	for  := range  {
		var (
			    string
			 string
			   net.Addr
			     error
		)
		switch  := .ChannelType();  {
		case "forwarded-tcpip":
			var  forwardedTCPPayload
			if  = Unmarshal(.ExtraData(), &);  != nil {
				.Reject(ConnectionFailed, "could not parse forwarded-tcpip payload: "+.Error())
				continue
			}

			// RFC 4254 section 7.2 specifies that incoming addresses should
			// list the address that was connected, in string format. It is the
			// same address used in the tcpip-forward request. The originator
			// address is an IP address instead.
			 = net.JoinHostPort(.Addr, strconv.FormatUint(uint64(.Port), 10))

			,  = parseTCPAddr(.OriginAddr, .OriginPort)
			if  != nil {
				.Reject(ConnectionFailed, .Error())
				continue
			}
			 = "tcp"
		case "forwarded-streamlocal@openssh.com":
			var  forwardedStreamLocalPayload
			if  = Unmarshal(.ExtraData(), &);  != nil {
				.Reject(ConnectionFailed, "could not parse forwarded-streamlocal@openssh.com payload: "+.Error())
				continue
			}
			 = .SocketPath
			 = &net.UnixAddr{
				Name: "@",
				Net:  "unix",
			}
			 = "unix"
		default:
			panic(fmt.Errorf("ssh: unknown channel type %s", ))
		}
		if  := .forward(, , , ); ! {
			// Section 7.2, implementations MUST reject spurious incoming
			// connections.
			.Reject(Prohibited, "no forward for address")
			continue
		}

	}
}

// remove removes the forward entry, and the channel feeding its
// listener.
func ( *forwardList) (,  string) {
	.Lock()
	defer .Unlock()
	for ,  := range .entries {
		if  == .network &&  == .addr {
			.entries = append(.entries[:], .entries[+1:]...)
			close(.c)
			return
		}
	}
}

// closeAll closes and clears all forwards.
func ( *forwardList) () {
	.Lock()
	defer .Unlock()
	for ,  := range .entries {
		close(.c)
	}
	.entries = nil
}

func ( *forwardList) (,  string,  net.Addr,  NewChannel) bool {
	.Lock()
	defer .Unlock()
	for ,  := range .entries {
		if  == .network &&  == .addr {
			.c <- forward{newCh: , raddr: }
			return true
		}
	}
	return false
}

type tcpListener struct {
	laddr *net.TCPAddr
	addr  string

	conn *Client
	in   <-chan forward
}

// Accept waits for and returns the next connection to the listener.
func ( *tcpListener) () (net.Conn, error) {
	,  := <-.in
	if ! {
		return nil, io.EOF
	}
	, ,  := .newCh.Accept()
	if  != nil {
		return nil, 
	}
	go DiscardRequests()

	return &chanConn{
		Channel: ,
		laddr:   .laddr,
		raddr:   .raddr,
	}, nil
}

// Close closes the listener.
func ( *tcpListener) () error {
	, ,  := net.SplitHostPort(.addr)
	if  != nil {
		return 
	}
	,  := strconv.ParseUint(, 10, 32)
	if  != nil {
		return 
	}
	 := channelForwardMsg{
		,
		uint32(),
	}

	// this also closes the listener.
	.conn.forwards.remove("tcp", .addr)
	, ,  := .conn.SendRequest("cancel-tcpip-forward", true, Marshal(&))
	if  == nil && ! {
		 = errors.New("ssh: cancel-tcpip-forward failed")
	}
	return 
}

// Addr returns the listener's network address.
func ( *tcpListener) () net.Addr {
	return .laddr
}

// DialContext initiates a connection to the addr from the remote host.
//
// The provided Context must be non-nil. If the context expires before the
// connection is complete, an error is returned. Once successfully connected,
// any expiration of the context will not affect the connection.
//
// See func Dial for additional information.
func ( *Client) ( context.Context, ,  string) (net.Conn, error) {
	if  := .Err();  != nil {
		return nil, 
	}
	type  struct {
		 net.Conn
		  error
	}
	 := make(chan )
	go func() {
		,  := .Dial(, )
		select {
		case  <- {, }:
		case <-.Done():
			if  != nil {
				.Close()
			}
		}
	}()
	select {
	case  := <-:
		return ., .
	case <-.Done():
		return nil, .Err()
	}
}

// Dial initiates a connection to the addr from the remote host.
// The resulting connection has a zero LocalAddr() and RemoteAddr().
func ( *Client) (,  string) (net.Conn, error) {
	var  Channel
	switch  {
	case "tcp", "tcp4", "tcp6":
		// Parse the address into host and numeric port.
		, ,  := net.SplitHostPort()
		if  != nil {
			return nil, 
		}
		,  := strconv.ParseUint(, 10, 16)
		if  != nil {
			return nil, 
		}
		,  = .dial(net.IPv4zero.String(), 0, , int())
		if  != nil {
			return nil, 
		}
		// Use a zero address for local and remote address.
		 := &net.TCPAddr{
			IP:   net.IPv4zero,
			Port: 0,
		}
		return &chanConn{
			Channel: ,
			laddr:   ,
			raddr:   ,
		}, nil
	case "unix":
		var  error
		,  = .dialStreamLocal()
		if  != nil {
			return nil, 
		}
		return &chanConn{
			Channel: ,
			laddr: &net.UnixAddr{
				Name: "@",
				Net:  "unix",
			},
			raddr: &net.UnixAddr{
				Name: ,
				Net:  "unix",
			},
		}, nil
	default:
		return nil, fmt.Errorf("ssh: unsupported protocol: %s", )
	}
}

// DialTCP connects to the remote address raddr on the network net,
// which must be "tcp", "tcp4", or "tcp6".  If laddr is not nil, it is used
// as the local address for the connection.
func ( *Client) ( string, ,  *net.TCPAddr) (net.Conn, error) {
	if  == nil {
		 = &net.TCPAddr{
			IP:   net.IPv4zero,
			Port: 0,
		}
	}
	,  := .dial(.IP.String(), .Port, .IP.String(), .Port)
	if  != nil {
		return nil, 
	}
	return &chanConn{
		Channel: ,
		laddr:   ,
		raddr:   ,
	}, nil
}

// RFC 4254 7.2
type channelOpenDirectMsg struct {
	raddr string
	rport uint32
	laddr string
	lport uint32
}

func ( *Client) ( string,  int,  string,  int) (Channel, error) {
	 := channelOpenDirectMsg{
		raddr: ,
		rport: uint32(),
		laddr: ,
		lport: uint32(),
	}
	, ,  := .OpenChannel("direct-tcpip", Marshal(&))
	if  != nil {
		return nil, 
	}
	go DiscardRequests()
	return , nil
}

type tcpChan struct {
	Channel // the backing channel
}

// chanConn fulfills the net.Conn interface without
// the tcpChan having to hold laddr or raddr directly.
type chanConn struct {
	Channel
	laddr, raddr net.Addr
}

// LocalAddr returns the local network address.
func ( *chanConn) () net.Addr {
	return .laddr
}

// RemoteAddr returns the remote network address.
func ( *chanConn) () net.Addr {
	return .raddr
}

// SetDeadline sets the read and write deadlines associated
// with the connection.
func ( *chanConn) ( time.Time) error {
	if  := .SetReadDeadline();  != nil {
		return 
	}
	return .SetWriteDeadline()
}

// SetReadDeadline sets the read deadline.
// A zero value for t means Read will not time out.
// After the deadline, the error from Read will implement net.Error
// with Timeout() == true.
func ( *chanConn) ( time.Time) error {
	// for compatibility with previous version,
	// the error message contains "tcpChan"
	return errors.New("ssh: tcpChan: deadline not supported")
}

// SetWriteDeadline exists to satisfy the net.Conn interface
// but is not implemented by this type.  It always returns an error.
func ( *chanConn) ( time.Time) error {
	return errors.New("ssh: tcpChan: deadline not supported")
}