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 .Close()
		defer .Close()
		io.Copy(, )
	}()
	go func() {
		defer .Close()
		defer .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
}

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 {
			// TODO: log parse failure
			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 {
			// TODO: log listen failure
			return false, []byte{}
		}
		, ,  := net.SplitHostPort(.Addr().String())
		,  := strconv.Atoi()
		.Lock()
		.forwards[] = 
		.Unlock()
		go func() {
			<-.Done()
			.Lock()
			,  := .forwards[]
			.Unlock()
			if  {
				.Close()
			}
		}()
		go func() {
			for {
				,  := .Accept()
				if  != nil {
					// TODO: log accept failure
					break
				}
				, ,  := net.SplitHostPort(.RemoteAddr().String())
				,  := strconv.Atoi()
				 := gossh.Marshal(&remoteForwardChannelData{
					DestAddr:   .BindAddr,
					DestPort:   uint32(),
					OriginAddr: ,
					OriginPort: uint32(),
				})
				go func() {
					, ,  := .OpenChannel(forwardedTCPChannelType, )
					if  != nil {
						// TODO: log failure to open channel
						log.Println()
						.Close()
						return
					}
					go gossh.DiscardRequests()
					go func() {
						defer .Close()
						defer .Close()
						io.Copy(, )
					}()
					go func() {
						defer .Close()
						defer .Close()
						io.Copy(, )
					}()
				}()
			}
			.Lock()
			delete(.forwards, )
			.Unlock()
		}()
		return true, gossh.Marshal(&remoteForwardSuccess{uint32()})

	case "cancel-tcpip-forward":
		var  remoteForwardCancelRequest
		if  := gossh.Unmarshal(.Payload, &);  != nil {
			// TODO: log parse failure
			return false, []byte{}
		}
		 := net.JoinHostPort(.BindAddr, strconv.Itoa(int(.BindPort)))
		.Lock()
		,  := .forwards[]
		.Unlock()
		if  {
			.Close()
		}
		return true, nil
	default:
		return false, nil
	}
}