Skip to content

Commit

Permalink
Add experimental udp tunneling method
Browse files Browse the repository at this point in the history
  • Loading branch information
yosebyte authored Dec 5, 2024
1 parent 9bfdaa5 commit 16d1db7
Show file tree
Hide file tree
Showing 5 changed files with 247 additions and 91 deletions.
2 changes: 0 additions & 2 deletions internal/forward/tcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 }()
Expand All @@ -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)
Expand Down
66 changes: 57 additions & 9 deletions internal/tunnel/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"net"
"net/url"
"strings"
"time"

"github.com/yosebyte/passport/internal/util"
"github.com/yosebyte/passport/pkg/log"
Expand All @@ -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
Expand All @@ -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 {
Expand All @@ -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]<TCP>\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]<UDP>\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
}
88 changes: 8 additions & 80 deletions internal/tunnel/server.go
Original file line number Diff line number Diff line change
@@ -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
}
88 changes: 88 additions & 0 deletions internal/tunnel/tcp.go
Original file line number Diff line number Diff line change
@@ -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]<TCP>\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
}
Loading

0 comments on commit 16d1db7

Please sign in to comment.