From 16d1db7e728df8fa4eb1406281c28b74255c64fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=90=98=F0=9D=90=A8=F0=9D=90=AC=F0=9D=90=9E?= =?UTF-8?q?=F0=9D=90=9B=F0=9D=90=B2=F0=9D=90=AD=F0=9D=90=9E?= <54016243+yosebyte@users.noreply.github.com> Date: Thu, 5 Dec 2024 08:11:14 +0000 Subject: [PATCH] Add experimental udp tunneling method --- internal/forward/tcp.go | 2 - internal/tunnel/client.go | 66 +++++++++++++++++++++++---- internal/tunnel/server.go | 88 ++++-------------------------------- internal/tunnel/tcp.go | 88 ++++++++++++++++++++++++++++++++++++ internal/tunnel/udp.go | 94 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 247 insertions(+), 91 deletions(-) create mode 100644 internal/tunnel/tcp.go create mode 100644 internal/tunnel/udp.go diff --git a/internal/forward/tcp.go b/internal/forward/tcp.go index 348882e..78f8a3a 100644 --- a/internal/forward/tcp.go +++ b/internal/forward/tcp.go @@ -36,7 +36,6 @@ func HandleTCP(parsedURL *url.URL, whiteList *sync.Map) error { time.Sleep(1 * time.Second) continue } - linkConn.SetNoDelay(true) semaphore <- struct{}{} go func(linkConn *net.TCPConn) { defer func() { <-semaphore }() @@ -61,7 +60,6 @@ func HandleTCP(parsedURL *url.URL, whiteList *sync.Map) error { linkConn.Close() return } - targetConn.SetNoDelay(true) log.Info("Target connection established: [%v]", targetAddr) log.Info("Starting data exchange: [%v] <-> [%v]", clientAddr, targetAddr) util.HandleConn(linkConn, targetConn) diff --git a/internal/tunnel/client.go b/internal/tunnel/client.go index 76509e3..fb00315 100644 --- a/internal/tunnel/client.go +++ b/internal/tunnel/client.go @@ -4,6 +4,7 @@ import ( "net" "net/url" "strings" + "time" "github.com/yosebyte/passport/internal/util" "github.com/yosebyte/passport/pkg/log" @@ -15,7 +16,12 @@ func Client(parsedURL *url.URL) error { log.Error("Unable to resolve link address: %v", parsedURL.Host) return err } - targetAddr, err := net.ResolveTCPAddr("tcp", strings.TrimPrefix(parsedURL.Path, "/")) + targetTCPAddr, err := net.ResolveTCPAddr("tcp", strings.TrimPrefix(parsedURL.Path, "/")) + if err != nil { + log.Error("Unable to resolve target address: %v", strings.TrimPrefix(parsedURL.Path, "/")) + return err + } + targetUDPAddr, err := net.ResolveUDPAddr("udp", strings.TrimPrefix(parsedURL.Path, "/")) if err != nil { log.Error("Unable to resolve target address: %v", strings.TrimPrefix(parsedURL.Path, "/")) return err @@ -26,7 +32,6 @@ func Client(parsedURL *url.URL) error { return err } defer linkConn.Close() - linkConn.SetNoDelay(true) log.Info("Tunnel connection established to: [%v]", linkAddr) buffer := make([]byte, 1024) for { @@ -35,26 +40,69 @@ func Client(parsedURL *url.URL) error { log.Error("Unable to read form link address: [%v] %v", linkAddr, err) break } - if string(buffer[:n]) == "[PASSPORT]\n" { + if string(buffer[:n]) == "[PASSPORT]\n" { go func() { - targetConn, err := net.DialTCP("tcp", nil, targetAddr) + targetConn, err := net.DialTCP("tcp", nil, targetTCPAddr) if err != nil { - log.Error("Unable to dial target address: [%v], %v", targetAddr, err) + log.Error("Unable to dial target address: [%v], %v", targetTCPAddr, err) return } - targetConn.SetNoDelay(true) - log.Info("Target connection established: [%v]", targetAddr) + log.Info("Target connection established: [%v]", targetTCPAddr) remoteConn, err := net.DialTCP("tcp", nil, linkAddr) if err != nil { log.Error("Unable to dial target address: [%v], %v", linkAddr, err) return } - remoteConn.SetNoDelay(true) - log.Info("Starting data exchange: [%v] <-> [%v]", linkAddr, targetAddr) + log.Info("Starting data exchange: [%v] <-> [%v]", linkAddr, targetTCPAddr) util.HandleConn(remoteConn, targetConn) log.Info("Connection closed successfully") }() } + if string(buffer[:n]) == "[PASSPORT]\n" { + go func() { + remoteConn, err := net.DialTCP("tcp", nil, linkAddr) + if err != nil { + log.Error("Unable to dial target address: [%v], %v", linkAddr, err) + return + } + log.Info("Remote connection established: [%v]", linkAddr) + buffer := make([]byte, 8192) + n, err := remoteConn.Read(buffer) + if err != nil { + log.Error("Unable to read from remote address: [%v] %v", linkAddr, err) + return + } + targetConn, err := net.DialUDP("udp", nil, targetUDPAddr) + if err != nil { + log.Error("Unable to dial target address: [%v] %v", targetUDPAddr, err) + return + } + defer targetConn.Close() + log.Info("Target connection established: [%v]", targetUDPAddr) + err = targetConn.SetDeadline(time.Now().Add(5 * time.Second)) + if err != nil { + log.Error("Unable to set deadline: %v", err) + return + } + log.Info("Starting data transfer: [%v] <-> [%v]", linkAddr, targetUDPAddr) + _, err = targetConn.Write(buffer[:n]) + if err != nil { + log.Error("Unable to write to target address: [%v] %v", targetUDPAddr, err) + return + } + n, _, err = targetConn.ReadFromUDP(buffer) + if err != nil { + log.Error("Unable to read from target address: [%v] %v", targetUDPAddr, err) + return + } + _, err = remoteConn.Write(buffer[:n]) + if err != nil { + log.Error("Unable to write to remote address: [%v] %v", linkAddr, err) + return + } + log.Info("Transfer completed successfully") + }() + } } return nil } diff --git a/internal/tunnel/server.go b/internal/tunnel/server.go index 9b3192a..d3474e3 100644 --- a/internal/tunnel/server.go +++ b/internal/tunnel/server.go @@ -1,89 +1,17 @@ package tunnel import ( - "net" "net/url" - "strings" "sync" - - "github.com/yosebyte/passport/internal/util" - "github.com/yosebyte/passport/pkg/log" ) func Server(parsedURL *url.URL, whiteList *sync.Map) error { - linkAddr, err := net.ResolveTCPAddr("tcp", parsedURL.Host) - if err != nil { - log.Error("Unable to resolve link address: %v", parsedURL.Host) - return err - } - targetAddr, err := net.ResolveTCPAddr("tcp", strings.TrimPrefix(parsedURL.Path, "/")) - if err != nil { - log.Error("Unable to resolve target address: %v", strings.TrimPrefix(parsedURL.Path, "/")) - return err - } - linkListen, err := net.ListenTCP("tcp", linkAddr) - if err != nil { - log.Error("Unable to listen link address: [%v]", linkAddr) - return err - } - defer linkListen.Close() - targetListen, err := net.ListenTCP("tcp", targetAddr) - if err != nil { - log.Error("Unable to listen target address: [%v]", targetAddr) - return err - } - defer targetListen.Close() - linkConn, err := linkListen.AcceptTCP() - if err != nil { - log.Error("Unable to accept connections form link address: [%v]", linkAddr) - return err - } - defer linkConn.Close() - log.Info("Tunnel connection established from: [%v]", linkConn.RemoteAddr().String()) - var mu sync.Mutex - semaphore := make(chan struct{}, 1024) - for { - targetConn, err := targetListen.AcceptTCP() - if err != nil { - log.Error("Unable to accept connections form target address: [%v] %v", targetAddr, err) - break - } - targetConn.SetNoDelay(true) - clientAddr := targetConn.RemoteAddr().String() - log.Info("Target connection established from: [%v]", clientAddr) - if parsedURL.Fragment != "" { - clientIP, _, err := net.SplitHostPort(clientAddr) - if err != nil { - log.Error("Unable to extract client IP address: [%v] %v", clientAddr, err) - targetConn.Close() - continue - } - if _, exists := whiteList.Load(clientIP); !exists { - log.Warn("Unauthorized IP address blocked: [%v]", clientIP) - targetConn.Close() - continue - } - } - semaphore <- struct{}{} - go func(targetConn *net.TCPConn) { - defer func() { <-semaphore }() - mu.Lock() - _, err = linkConn.Write([]byte("[PASSPORT]\n")) - mu.Unlock() - if err != nil { - log.Error("Unable to send signal: %v", err) - targetConn.Close() - return - } - remoteConn, err := linkListen.AcceptTCP() - if err != nil { - log.Error("Unable to accept connections form link address: [%v] %v", linkAddr, err) - return - } - log.Info("Starting data exchange: [%v] <-> [%v]", clientAddr, targetAddr) - util.HandleConn(remoteConn, targetConn) - log.Info("Connection closed successfully") - }(targetConn) - } - return nil + errChan := make(chan error, 2) + go func() { + errChan <- ServeTCP(parsedURL, whiteList) + }() + go func() { + errChan <- ServeUDP(parsedURL, whiteList) + }() + return <-errChan } diff --git a/internal/tunnel/tcp.go b/internal/tunnel/tcp.go new file mode 100644 index 0000000..580dc5a --- /dev/null +++ b/internal/tunnel/tcp.go @@ -0,0 +1,88 @@ +package tunnel + +import ( + "net" + "net/url" + "strings" + "sync" + + "github.com/yosebyte/passport/internal/util" + "github.com/yosebyte/passport/pkg/log" +) + +func ServeTCP(parsedURL *url.URL, whiteList *sync.Map) error { + linkAddr, err := net.ResolveTCPAddr("tcp", parsedURL.Host) + if err != nil { + log.Error("Unable to resolve link address: %v", parsedURL.Host) + return err + } + targetAddr, err := net.ResolveTCPAddr("tcp", strings.TrimPrefix(parsedURL.Path, "/")) + if err != nil { + log.Error("Unable to resolve target address: %v", strings.TrimPrefix(parsedURL.Path, "/")) + return err + } + linkListen, err := net.ListenTCP("tcp", linkAddr) + if err != nil { + log.Error("Unable to listen link address: [%v]", linkAddr) + return err + } + defer linkListen.Close() + targetListen, err := net.ListenTCP("tcp", targetAddr) + if err != nil { + log.Error("Unable to listen target address: [%v]", targetAddr) + return err + } + defer targetListen.Close() + linkConn, err := linkListen.AcceptTCP() + if err != nil { + log.Error("Unable to accept connections form link address: [%v]", linkAddr) + return err + } + defer linkConn.Close() + log.Info("Tunnel connection established from: [%v]", linkConn.RemoteAddr().String()) + var mu sync.Mutex + semaphore := make(chan struct{}, 1024) + for { + targetConn, err := targetListen.AcceptTCP() + if err != nil { + log.Error("Unable to accept connections form target address: [%v] %v", targetAddr, err) + break + } + clientAddr := targetConn.RemoteAddr().String() + log.Info("Target connection established from: [%v]", clientAddr) + if parsedURL.Fragment != "" { + clientIP, _, err := net.SplitHostPort(clientAddr) + if err != nil { + log.Error("Unable to extract client IP address: [%v] %v", clientAddr, err) + targetConn.Close() + continue + } + if _, exists := whiteList.Load(clientIP); !exists { + log.Warn("Unauthorized IP address blocked: [%v]", clientIP) + targetConn.Close() + continue + } + } + semaphore <- struct{}{} + go func(targetConn *net.TCPConn) { + defer func() { <-semaphore }() + mu.Lock() + _, err = linkConn.Write([]byte("[PASSPORT]\n")) + mu.Unlock() + if err != nil { + log.Error("Unable to send signal: %v", err) + targetConn.Close() + return + } + remoteConn, err := linkListen.AcceptTCP() + if err != nil { + log.Error("Unable to accept connections form link address: [%v] %v", linkAddr, err) + return + } + log.Info("Starting data exchange: [%v] <-> [%v]", clientAddr, targetAddr) + util.HandleConn(remoteConn, targetConn) + log.Info("Connection closed successfully") + }(targetConn) + } + return nil +} diff --git a/internal/tunnel/udp.go b/internal/tunnel/udp.go new file mode 100644 index 0000000..7c1eaba --- /dev/null +++ b/internal/tunnel/udp.go @@ -0,0 +1,94 @@ +package tunnel + +import ( + "net" + "net/url" + "strings" + "sync" + + "github.com/yosebyte/passport/pkg/log" +) + +func ServeUDP(parsedURL *url.URL, whiteList *sync.Map) error { + linkAddr, err := net.ResolveTCPAddr("tcp", parsedURL.Host) + if err != nil { + log.Error("Unable to resolve link address: %v", parsedURL.Host) + return err + } + targetAddr, err := net.ResolveUDPAddr("udp", strings.TrimPrefix(parsedURL.Path, "/")) + if err != nil { + log.Error("Unable to resolve target address: %v", strings.TrimPrefix(parsedURL.Path, "/")) + return err + } + linkListen, err := net.ListenTCP("tcp", linkAddr) + if err != nil { + log.Error("Unable to listen link address: [%v]", linkAddr) + return err + } + defer linkListen.Close() + linkConn, err := linkListen.AcceptTCP() + if err != nil { + log.Error("Unable to accept connections form link address: [%v]", linkAddr) + return err + } + defer linkConn.Close() + log.Info("Tunnel connection established from: [%v]", linkConn.RemoteAddr().String()) + targetConn, err := net.ListenUDP("udp", targetAddr) + if err != nil { + log.Error("Unable to listen target address: [%v]", targetAddr) + return err + } + defer targetConn.Close() + var mu sync.Mutex + semaphore := make(chan struct{}, 1024) + for { + buffer := make([]byte, 8192) + n, clientAddr, err := targetConn.ReadFromUDP(buffer) + if err != nil { + log.Error("Unable to read from client address: [%v] %v", clientAddr, err) + continue + } + if parsedURL.Fragment != "" { + clientIP := clientAddr.IP.String() + if _, exists := whiteList.Load(clientIP); !exists { + log.Warn("Unauthorized IP address blocked: [%v]", clientIP) + continue + } + } + semaphore <- struct{}{} + go func(buffer []byte, n int, clientAddr *net.UDPAddr) { + defer func() { <-semaphore }() + mu.Lock() + _, err = linkConn.Write([]byte("[PASSPORT]\n")) + mu.Unlock() + if err != nil { + log.Error("Unable to send signal: %v", err) + targetConn.Close() + return + } + remoteConn, err := linkListen.AcceptTCP() + if err != nil { + log.Error("Unable to accept connections form link address: [%v] %v", linkAddr, err) + return + } + defer remoteConn.Close() + log.Info("Starting data transfer: [%v] <-> [%v]", clientAddr, targetAddr) + _, err = remoteConn.Write(buffer[:n]) + if err != nil { + log.Error("Unable to write to link address: [%v] %v", linkAddr, err) + return + } + n, err = remoteConn.Read(buffer) + if err != nil { + log.Error("Unable to read from link address: [%v] %v", linkAddr, err) + return + } + _, err = targetConn.WriteToUDP(buffer[:n], clientAddr) + if err != nil { + log.Error("Unable to write to client address: [%v] %v", clientAddr, err) + return + } + log.Info("Transfer completed successfully") + }(buffer, n, clientAddr) + } +}