package ssh

import (
	
	
	
	
	

	gossh 
)

const (
	forwardedTCPChannelType = "forwarded-tcpip"
)

// direct-tcpip data struct as specified in RFC4254, Section 7.2.
type localForwardChannelData struct {
	DestAddr string
	DestPort uint32

	OriginAddr string
	OriginPort uint32
}

// DirectTCPIPHandler can be enabled by adding it to the server's
// ChannelHandlers under direct-tcpip.
func ( *Server,  *gossh.ServerConn,  gossh.NewChannel,  Context) {
	 := localForwardChannelData{}
	if  := gossh.Unmarshal(.ExtraData(), &);  != nil {
		_ = .Reject(gossh.ConnectionFailed, "error parsing forward data: "+.Error())
		return
	}

	if .LocalPortForwardingCallback == nil || !.LocalPortForwardingCallback(, .DestAddr, .DestPort) {
		_ = .Reject(gossh.Prohibited, "port forwarding is disabled")
		return
	}

	 := net.JoinHostPort(.DestAddr, strconv.FormatInt(int64(.DestPort), 10))

	var  net.Dialer
	,  := .DialContext(, "tcp", )
	if  != nil {
		_ = .Reject(gossh.ConnectionFailed, .Error())
		return
	}

	, ,  := .Accept()
	if  != nil {
		_ = .Close()
		return
	}
	go gossh.DiscardRequests()

	go func() {
		defer recoverAndLog("panic proxying forwarded connection", nil, nil)
		defer func() { _ = .Close() }()
		defer func() { _ = .Close() }()
		_, _ = io.Copy(, )
	}()
	go func() {
		defer recoverAndLog("panic proxying forwarded connection", nil, nil)
		defer func() { _ = .Close() }()
		defer func() { _ = .Close() }()
		_, _ = io.Copy(, )
	}()
}

type remoteForwardRequest struct {
	BindAddr string
	BindPort uint32
}

type remoteForwardSuccess struct {
	BindPort uint32
}

type remoteForwardCancelRequest struct {
	BindAddr string
	BindPort uint32
}

type remoteForwardChannelData struct {
	DestAddr   string
	DestPort   uint32
	OriginAddr string
	OriginPort uint32
}

// ForwardedTCPHandler can be enabled by creating a ForwardedTCPHandler and
// adding the HandleSSHRequest callback to the server's RequestHandlers under
// tcpip-forward and cancel-tcpip-forward.
type ForwardedTCPHandler struct {
	forwards map[string]net.Listener
	sync.Mutex
}

// HandleSSHRequest handles the tcpip-forward and cancel-tcpip-forward
// global requests.
func ( *ForwardedTCPHandler) ( Context,  *Server,  *gossh.Request) (bool, []byte) {
	.Lock()
	if .forwards == nil {
		.forwards = make(map[string]net.Listener)
	}
	.Unlock()
	 := .Value(ContextKeyConn).(*gossh.ServerConn)
	switch .Type {
	case "tcpip-forward":
		var  remoteForwardRequest
		if  := gossh.Unmarshal(.Payload, &);  != nil {
			slog.Warn("ssh: failed to parse tcpip-forward request", "err", )
			return false, []byte{}
		}
		if .ReversePortForwardingCallback == nil || !.ReversePortForwardingCallback(, .BindAddr, .BindPort) {
			return false, []byte("port forwarding is disabled")
		}
		 := net.JoinHostPort(.BindAddr, strconv.Itoa(int(.BindPort)))
		,  := net.Listen("tcp", )
		if  != nil {
			slog.Warn("ssh: reverse port forward listen failed", "addr", , "err", )
			return false, []byte{}
		}
		, ,  := net.SplitHostPort(.Addr().String())
		,  := strconv.Atoi()
		// Use the actual bound port as the map key so that port 0
		// requests don't collide.
		 = net.JoinHostPort(.BindAddr, )
		.Lock()
		.forwards[] = 
		.Unlock()
		go func() {
			<-.Done()
			.Lock()
			,  := .forwards[]
			.Unlock()
			if  {
				_ = .Close()
			}
		}()
		go func() {
			for {
				,  := .Accept()
				if  != nil {
					slog.Debug("ssh: reverse port forward accept failed", "addr", , "err", )
					break
				}
				, ,  := net.SplitHostPort(.RemoteAddr().String())
				,  := strconv.Atoi()
				 := gossh.Marshal(&remoteForwardChannelData{
					DestAddr:   .BindAddr,
					DestPort:   uint32(),
					OriginAddr: ,
					OriginPort: uint32(),
				})
				go func() {
					defer recoverAndLog("panic opening forwarded channel", nil, func() {
						_ = .Close()
					})
					, ,  := .OpenChannel(forwardedTCPChannelType, )
					if  != nil {
						slog.Warn("ssh: failed to open forwarded channel", "err", )
						_ = .Close()
						return
					}
					go gossh.DiscardRequests()
					go func() {
						defer recoverAndLog("panic proxying forwarded channel", nil, nil)
						defer func() { _ = .Close() }()
						defer func() { _ = .Close() }()
						_, _ = io.Copy(, )
					}()
					go func() {
						defer recoverAndLog("panic proxying forwarded channel", nil, nil)
						defer func() { _ = .Close() }()
						defer func() { _ = .Close() }()
						_, _ = io.Copy(, )
					}()
				}()
			}
			.Lock()
			delete(.forwards, )
			.Unlock()
		}()
		return true, gossh.Marshal(&remoteForwardSuccess{uint32()})

	case "cancel-tcpip-forward":
		var  remoteForwardCancelRequest
		if  := gossh.Unmarshal(.Payload, &);  != nil {
			slog.Warn("ssh: failed to parse cancel-tcpip-forward request", "err", )
			return false, []byte{}
		}
		 := net.JoinHostPort(.BindAddr, strconv.Itoa(int(.BindPort)))
		.Lock()
		,  := .forwards[]
		.Unlock()
		if  {
			_ = .Close()
		}
		return true, nil
	default:
		return false, nil
	}
}