// 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".
func ( *Client) (,  string) (net.Listener, error) {
	switch  {
	case "tcp", "tcp4", "tcp6":
		,  := net.ResolveTCPAddr(, )
		if  != nil {
			return nil, 
		}
		return .ListenTCP()
	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.
func ( *Client) ( *net.TCPAddr) (net.Listener, error) {
	.handleForwardsOnce.Do(.handleForwards)
	if .Port == 0 && isBrokenOpenSSHVersion(string(.ServerVersion())) {
		return .autoPortListenWorkaround()
	}

	 := channelForwardMsg{
		.IP.String(),
		uint32(.Port),
	}
	// 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 .Port == 0 {
		var  struct {
			 uint32
		}
		if  := Unmarshal(, &);  != nil {
			return nil, 
		}
		.Port = int(.)
	}

	// Register this forward, using the port number we obtained.
	 := .forwards.add()

	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 {
	laddr net.Addr
	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) ( net.Addr) chan forward {
	.Lock()
	defer .Unlock()
	 := forwardEntry{
		laddr: ,
		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", )
	}
	 := net.ParseIP(string())
	if  == nil {
		return nil, fmt.Errorf("ssh: cannot parse IP address %q", )
	}
	return &net.TCPAddr{IP: , Port: int()}, nil
}

func ( *forwardList) ( <-chan NewChannel) {
	for  := range  {
		var (
			 net.Addr
			 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, in string
			// format. It is implied that this should be an IP
			// address, as it would be impossible to connect to it
			// otherwise.
			,  = parseTCPAddr(.Addr, .Port)
			if  != nil {
				.Reject(ConnectionFailed, .Error())
				continue
			}
			,  = parseTCPAddr(.OriginAddr, .OriginPort)
			if  != nil {
				.Reject(ConnectionFailed, .Error())
				continue
			}

		case "forwarded-streamlocal@openssh.com":
			var  forwardedStreamLocalPayload
			if  = Unmarshal(.ExtraData(), &);  != nil {
				.Reject(ConnectionFailed, "could not parse forwarded-streamlocal@openssh.com payload: "+.Error())
				continue
			}
			 = &net.UnixAddr{
				Name: .SocketPath,
				Net:  "unix",
			}
			 = &net.UnixAddr{
				Name: "@",
				Net:  "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) ( net.Addr) {
	.Lock()
	defer .Unlock()
	for ,  := range .entries {
		if .Network() == .laddr.Network() && .String() == .laddr.String() {
			.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) (,  net.Addr,  NewChannel) bool {
	.Lock()
	defer .Unlock()
	for ,  := range .entries {
		if .Network() == .laddr.Network() && .String() == .laddr.String() {
			.c <- forward{newCh: , raddr: }
			return true
		}
	}
	return false
}

type tcpListener struct {
	laddr *net.TCPAddr

	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 {
	 := channelForwardMsg{
		.laddr.IP.String(),
		uint32(.laddr.Port),
	}

	// this also closes the listener.
	.conn.forwards.remove(.laddr)
	, ,  := .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
}

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

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")
}