mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-03 08:02:36 +09:00 
			
		
		
		
	resolves git conflicts from #3896 (credit to @belak, in case github doesn't keep original author during squash) Co-Authored-By: Matti Ranta <techknowlogick@gitea.io>
		
			
				
	
	
		
			194 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
			
		
		
	
	
			194 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
		
			Vendored
		
	
	
	
package ssh
 | 
						|
 | 
						|
import (
 | 
						|
	"io"
 | 
						|
	"log"
 | 
						|
	"net"
 | 
						|
	"strconv"
 | 
						|
	"sync"
 | 
						|
 | 
						|
	gossh "golang.org/x/crypto/ssh"
 | 
						|
)
 | 
						|
 | 
						|
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 DirectTCPIPHandler(srv *Server, conn *gossh.ServerConn, newChan gossh.NewChannel, ctx Context) {
 | 
						|
	d := localForwardChannelData{}
 | 
						|
	if err := gossh.Unmarshal(newChan.ExtraData(), &d); err != nil {
 | 
						|
		newChan.Reject(gossh.ConnectionFailed, "error parsing forward data: "+err.Error())
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	if srv.LocalPortForwardingCallback == nil || !srv.LocalPortForwardingCallback(ctx, d.DestAddr, d.DestPort) {
 | 
						|
		newChan.Reject(gossh.Prohibited, "port forwarding is disabled")
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	dest := net.JoinHostPort(d.DestAddr, strconv.FormatInt(int64(d.DestPort), 10))
 | 
						|
 | 
						|
	var dialer net.Dialer
 | 
						|
	dconn, err := dialer.DialContext(ctx, "tcp", dest)
 | 
						|
	if err != nil {
 | 
						|
		newChan.Reject(gossh.ConnectionFailed, err.Error())
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	ch, reqs, err := newChan.Accept()
 | 
						|
	if err != nil {
 | 
						|
		dconn.Close()
 | 
						|
		return
 | 
						|
	}
 | 
						|
	go gossh.DiscardRequests(reqs)
 | 
						|
 | 
						|
	go func() {
 | 
						|
		defer ch.Close()
 | 
						|
		defer dconn.Close()
 | 
						|
		io.Copy(ch, dconn)
 | 
						|
	}()
 | 
						|
	go func() {
 | 
						|
		defer ch.Close()
 | 
						|
		defer dconn.Close()
 | 
						|
		io.Copy(dconn, ch)
 | 
						|
	}()
 | 
						|
}
 | 
						|
 | 
						|
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 (h *ForwardedTCPHandler) HandleSSHRequest(ctx Context, srv *Server, req *gossh.Request) (bool, []byte) {
 | 
						|
	h.Lock()
 | 
						|
	if h.forwards == nil {
 | 
						|
		h.forwards = make(map[string]net.Listener)
 | 
						|
	}
 | 
						|
	h.Unlock()
 | 
						|
	conn := ctx.Value(ContextKeyConn).(*gossh.ServerConn)
 | 
						|
	switch req.Type {
 | 
						|
	case "tcpip-forward":
 | 
						|
		var reqPayload remoteForwardRequest
 | 
						|
		if err := gossh.Unmarshal(req.Payload, &reqPayload); err != nil {
 | 
						|
			// TODO: log parse failure
 | 
						|
			return false, []byte{}
 | 
						|
		}
 | 
						|
		if srv.ReversePortForwardingCallback == nil || !srv.ReversePortForwardingCallback(ctx, reqPayload.BindAddr, reqPayload.BindPort) {
 | 
						|
			return false, []byte("port forwarding is disabled")
 | 
						|
		}
 | 
						|
		addr := net.JoinHostPort(reqPayload.BindAddr, strconv.Itoa(int(reqPayload.BindPort)))
 | 
						|
		ln, err := net.Listen("tcp", addr)
 | 
						|
		if err != nil {
 | 
						|
			// TODO: log listen failure
 | 
						|
			return false, []byte{}
 | 
						|
		}
 | 
						|
		_, destPortStr, _ := net.SplitHostPort(ln.Addr().String())
 | 
						|
		destPort, _ := strconv.Atoi(destPortStr)
 | 
						|
		h.Lock()
 | 
						|
		h.forwards[addr] = ln
 | 
						|
		h.Unlock()
 | 
						|
		go func() {
 | 
						|
			<-ctx.Done()
 | 
						|
			h.Lock()
 | 
						|
			ln, ok := h.forwards[addr]
 | 
						|
			h.Unlock()
 | 
						|
			if ok {
 | 
						|
				ln.Close()
 | 
						|
			}
 | 
						|
		}()
 | 
						|
		go func() {
 | 
						|
			for {
 | 
						|
				c, err := ln.Accept()
 | 
						|
				if err != nil {
 | 
						|
					// TODO: log accept failure
 | 
						|
					break
 | 
						|
				}
 | 
						|
				originAddr, orignPortStr, _ := net.SplitHostPort(c.RemoteAddr().String())
 | 
						|
				originPort, _ := strconv.Atoi(orignPortStr)
 | 
						|
				payload := gossh.Marshal(&remoteForwardChannelData{
 | 
						|
					DestAddr:   reqPayload.BindAddr,
 | 
						|
					DestPort:   uint32(destPort),
 | 
						|
					OriginAddr: originAddr,
 | 
						|
					OriginPort: uint32(originPort),
 | 
						|
				})
 | 
						|
				go func() {
 | 
						|
					ch, reqs, err := conn.OpenChannel(forwardedTCPChannelType, payload)
 | 
						|
					if err != nil {
 | 
						|
						// TODO: log failure to open channel
 | 
						|
						log.Println(err)
 | 
						|
						c.Close()
 | 
						|
						return
 | 
						|
					}
 | 
						|
					go gossh.DiscardRequests(reqs)
 | 
						|
					go func() {
 | 
						|
						defer ch.Close()
 | 
						|
						defer c.Close()
 | 
						|
						io.Copy(ch, c)
 | 
						|
					}()
 | 
						|
					go func() {
 | 
						|
						defer ch.Close()
 | 
						|
						defer c.Close()
 | 
						|
						io.Copy(c, ch)
 | 
						|
					}()
 | 
						|
				}()
 | 
						|
			}
 | 
						|
			h.Lock()
 | 
						|
			delete(h.forwards, addr)
 | 
						|
			h.Unlock()
 | 
						|
		}()
 | 
						|
		return true, gossh.Marshal(&remoteForwardSuccess{uint32(destPort)})
 | 
						|
 | 
						|
	case "cancel-tcpip-forward":
 | 
						|
		var reqPayload remoteForwardCancelRequest
 | 
						|
		if err := gossh.Unmarshal(req.Payload, &reqPayload); err != nil {
 | 
						|
			// TODO: log parse failure
 | 
						|
			return false, []byte{}
 | 
						|
		}
 | 
						|
		addr := net.JoinHostPort(reqPayload.BindAddr, strconv.Itoa(int(reqPayload.BindPort)))
 | 
						|
		h.Lock()
 | 
						|
		ln, ok := h.forwards[addr]
 | 
						|
		h.Unlock()
 | 
						|
		if ok {
 | 
						|
			ln.Close()
 | 
						|
		}
 | 
						|
		return true, nil
 | 
						|
	default:
 | 
						|
		return false, nil
 | 
						|
	}
 | 
						|
}
 |