text: // 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 (
  	"errors"
  	"fmt"
  	"io"
  	"math/rand"
  	"net"
  	"strconv"
  	"strings"
  	"sync"
  	"time"
  )
  
  // 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 (c *Client) Listen(n, addr string) (net.Listener, error) {
  	switch n {
  	case "tcp", "tcp4", "tcp6":
  		laddr, err := net.ResolveTCPAddr(n, addr)
  		if err != nil {
  			return nil, err
  		}
  		return c.ListenTCP(laddr)
  	case "unix":
  		return c.ListenUnix(addr)
  	default:
  		return nil, fmt.Errorf("ssh: unsupported protocol: %s", n)
  	}
  }
  
  // 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(versionStr string) bool {
  	i := strings.Index(versionStr, openSSHPrefix)
  	if i < 0 {
  		return false
  	}
  	i += len(openSSHPrefix)
  	j := i
  	for ; j < len(versionStr); j++ {
  		if versionStr[j] < '0' || versionStr[j] > '9' {
  			break
  		}
  	}
  	version, _ := strconv.Atoi(versionStr[i:j])
  	return version < 6
  }
  
  // autoPortListenWorkaround simulates automatic port allocation by
  // trying random ports repeatedly.
  func (c *Client) autoPortListenWorkaround(laddr *net.TCPAddr) (net.Listener, error) {
  	var sshListener net.Listener
  	var err error
  	const tries = 10
  	for i := 0; i < tries; i++ {
  		addr := *laddr
  		addr.Port = 1024 + portRandomizer.Intn(60000)
  		sshListener, err = c.ListenTCP(&addr)
  		if err == nil {
  			laddr.Port = addr.Port
  			return sshListener, err
  		}
  	}
  	return nil, fmt.Errorf("ssh: listen on random port failed after %d tries: %v", tries, err)
  }
  
  // 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 (c *Client) handleForwards() {
  	go c.forwards.handleChannels(c.HandleChannelOpen("forwarded-tcpip"))
  	go c.forwards.handleChannels(c.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 (c *Client) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) {
  	c.handleForwardsOnce.Do(c.handleForwards)
  	if laddr.Port == 0 && isBrokenOpenSSHVersion(string(c.ServerVersion())) {
  		return c.autoPortListenWorkaround(laddr)
  	}
  
  	m := channelForwardMsg{
  		laddr.IP.String(),
  		uint32(laddr.Port),
  	}
  	// send message
  	ok, resp, err := c.SendRequest("tcpip-forward", true, Marshal(&m))
  	if err != nil {
  		return nil, err
  	}
  	if !ok {
  		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 laddr.Port == 0 {
  		var p struct {
  			Port uint32
  		}
  		if err := Unmarshal(resp, &p); err != nil {
  			return nil, err
  		}
  		laddr.Port = int(p.Port)
  	}
  
  	// Register this forward, using the port number we obtained.
  	ch := c.forwards.add(laddr)
  
  	return &tcpListener{laddr, c, ch}, 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 (l *forwardList) add(addr net.Addr) chan forward {
  	l.Lock()
  	defer l.Unlock()
  	f := forwardEntry{
  		laddr: addr,
  		c:     make(chan forward, 1),
  	}
  	l.entries = append(l.entries, f)
  	return f.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(addr string, port uint32) (*net.TCPAddr, error) {
  	if port == 0 || port > 65535 {
  		return nil, fmt.Errorf("ssh: port number out of range: %d", port)
  	}
  	ip := net.ParseIP(string(addr))
  	if ip == nil {
  		return nil, fmt.Errorf("ssh: cannot parse IP address %q", addr)
  	}
  	return &net.TCPAddr{IP: ip, Port: int(port)}, nil
  }
  
  func (l *forwardList) handleChannels(in <-chan NewChannel) {
  	for ch := range in {
  		var (
  			laddr net.Addr
  			raddr net.Addr
  			err   error
  		)
  		switch channelType := ch.ChannelType(); channelType {
  		case "forwarded-tcpip":
  			var payload forwardedTCPPayload
  			if err = Unmarshal(ch.ExtraData(), &payload); err != nil {
  				ch.Reject(ConnectionFailed, "could not parse forwarded-tcpip payload: "+err.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.
  			laddr, err = parseTCPAddr(payload.Addr, payload.Port)
  			if err != nil {
  				ch.Reject(ConnectionFailed, err.Error())
  				continue
  			}
  			raddr, err = parseTCPAddr(payload.OriginAddr, payload.OriginPort)
  			if err != nil {
  				ch.Reject(ConnectionFailed, err.Error())
  				continue
  			}
  
  		case "forwarded-streamlocal@openssh.com":
  			var payload forwardedStreamLocalPayload
  			if err = Unmarshal(ch.ExtraData(), &payload); err != nil {
  				ch.Reject(ConnectionFailed, "could not parse forwarded-streamlocal@openssh.com payload: "+err.Error())
  				continue
  			}
  			laddr = &net.UnixAddr{
  				Name: payload.SocketPath,
  				Net:  "unix",
  			}
  			raddr = &net.UnixAddr{
  				Name: "@",
  				Net:  "unix",
  			}
  		default:
  			panic(fmt.Errorf("ssh: unknown channel type %s", channelType))
  		}
  		if ok := l.forward(laddr, raddr, ch); !ok {
  			// Section 7.2, implementations MUST reject spurious incoming
  			// connections.
  			ch.Reject(Prohibited, "no forward for address")
  			continue
  		}
  
  	}
  }
  
  // remove removes the forward entry, and the channel feeding its
  // listener.
  func (l *forwardList) remove(addr net.Addr) {
  	l.Lock()
  	defer l.Unlock()
  	for i, f := range l.entries {
  		if addr.Network() == f.laddr.Network() && addr.String() == f.laddr.String() {
  			l.entries = append(l.entries[:i], l.entries[i+1:]...)
  			close(f.c)
  			return
  		}
  	}
  }
  
  // closeAll closes and clears all forwards.
  func (l *forwardList) closeAll() {
  	l.Lock()
  	defer l.Unlock()
  	for _, f := range l.entries {
  		close(f.c)
  	}
  	l.entries = nil
  }
  
  func (l *forwardList) forward(laddr, raddr net.Addr, ch NewChannel) bool {
  	l.Lock()
  	defer l.Unlock()
  	for _, f := range l.entries {
  		if laddr.Network() == f.laddr.Network() && laddr.String() == f.laddr.String() {
  			f.c <- forward{newCh: ch, raddr: 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 (l *tcpListener) Accept() (net.Conn, error) {
  	s, ok := <-l.in
  	if !ok {
  		return nil, io.EOF
  	}
  	ch, incoming, err := s.newCh.Accept()
  	if err != nil {
  		return nil, err
  	}
  	go DiscardRequests(incoming)
  
  	return &chanConn{
  		Channel: ch,
  		laddr:   l.laddr,
  		raddr:   s.raddr,
  	}, nil
  }
  
  // Close closes the listener.
  func (l *tcpListener) Close() error {
  	m := channelForwardMsg{
  		l.laddr.IP.String(),
  		uint32(l.laddr.Port),
  	}
  
  	// this also closes the listener.
  	l.conn.forwards.remove(l.laddr)
  	ok, _, err := l.conn.SendRequest("cancel-tcpip-forward", true, Marshal(&m))
  	if err == nil && !ok {
  		err = errors.New("ssh: cancel-tcpip-forward failed")
  	}
  	return err
  }
  
  // Addr returns the listener's network address.
  func (l *tcpListener) Addr() net.Addr {
  	return l.laddr
  }
  
  // Dial initiates a connection to the addr from the remote host.
  // The resulting connection has a zero LocalAddr() and RemoteAddr().
  func (c *Client) Dial(n, addr string) (net.Conn, error) {
  	var ch Channel
  	switch n {
  	case "tcp", "tcp4", "tcp6":
  		// Parse the address into host and numeric port.
  		host, portString, err := net.SplitHostPort(addr)
  		if err != nil {
  			return nil, err
  		}
  		port, err := strconv.ParseUint(portString, 10, 16)
  		if err != nil {
  			return nil, err
  		}
  		ch, err = c.dial(net.IPv4zero.String(), 0, host, int(port))
  		if err != nil {
  			return nil, err
  		}
  		// Use a zero address for local and remote address.
  		zeroAddr := &net.TCPAddr{
  			IP:   net.IPv4zero,
  			Port: 0,
  		}
  		return &chanConn{
  			Channel: ch,
  			laddr:   zeroAddr,
  			raddr:   zeroAddr,
  		}, nil
  	case "unix":
  		var err error
  		ch, err = c.dialStreamLocal(addr)
  		if err != nil {
  			return nil, err
  		}
  		return &chanConn{
  			Channel: ch,
  			laddr: &net.UnixAddr{
  				Name: "@",
  				Net:  "unix",
  			},
  			raddr: &net.UnixAddr{
  				Name: addr,
  				Net:  "unix",
  			},
  		}, nil
  	default:
  		return nil, fmt.Errorf("ssh: unsupported protocol: %s", n)
  	}
  }
  
  // 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 (c *Client) DialTCP(n string, laddr, raddr *net.TCPAddr) (net.Conn, error) {
  	if laddr == nil {
  		laddr = &net.TCPAddr{
  			IP:   net.IPv4zero,
  			Port: 0,
  		}
  	}
  	ch, err := c.dial(laddr.IP.String(), laddr.Port, raddr.IP.String(), raddr.Port)
  	if err != nil {
  		return nil, err
  	}
  	return &chanConn{
  		Channel: ch,
  		laddr:   laddr,
  		raddr:   raddr,
  	}, nil
  }
  
  // RFC 4254 7.2
  type channelOpenDirectMsg struct {
  	raddr string
  	rport uint32
  	laddr string
  	lport uint32
  }
  
  func (c *Client) dial(laddr string, lport int, raddr string, rport int) (Channel, error) {
  	msg := channelOpenDirectMsg{
  		raddr: raddr,
  		rport: uint32(rport),
  		laddr: laddr,
  		lport: uint32(lport),
  	}
  	ch, in, err := c.OpenChannel("direct-tcpip", Marshal(&msg))
  	if err != nil {
  		return nil, err
  	}
  	go DiscardRequests(in)
  	return ch, err
  }
  
  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 (t *chanConn) LocalAddr() net.Addr {
  	return t.laddr
  }
  
  // RemoteAddr returns the remote network address.
  func (t *chanConn) RemoteAddr() net.Addr {
  	return t.raddr
  }
  
  // SetDeadline sets the read and write deadlines associated
  // with the connection.
  func (t *chanConn) SetDeadline(deadline time.Time) error {
  	if err := t.SetReadDeadline(deadline); err != nil {
  		return err
  	}
  	return t.SetWriteDeadline(deadline)
  }
  
  // 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 (t *chanConn) SetReadDeadline(deadline 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 (t *chanConn) SetWriteDeadline(deadline time.Time) error {
  	return errors.New("ssh: tcpChan: deadline not supported")
  }
  