mirror of
https://github.com/therootcompany/golib.git
synced 2025-12-12 00:18:46 +00:00
feat: add tcpfwd to pipe connections
This commit is contained in:
parent
0793b8f16f
commit
0784b58dba
86
cmd/tcpfwd/main.go
Normal file
86
cmd/tcpfwd/main.go
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var listenPort string
|
||||||
|
var target string
|
||||||
|
flag.StringVar(&listenPort, "port", "", "Local port to listen on (same as target by default)")
|
||||||
|
flag.StringVar(&target, "target", "", "Target host:port (required)")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if target == "" {
|
||||||
|
flag.Usage()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(listenPort) == 0 {
|
||||||
|
i := strings.LastIndex(target, ":")
|
||||||
|
listenPort = target[i+1:]
|
||||||
|
}
|
||||||
|
listenAddr := ":" + listenPort
|
||||||
|
log.Printf("TCP bridge %s → %s", listenAddr, target)
|
||||||
|
|
||||||
|
// Note: allow unprivileged users to use this like so:
|
||||||
|
// echo 'net.ipv4.ip_unprivileged_port_start=1' | sudo tee /etc/sysctl.d/01-deprivilege-ports.conf
|
||||||
|
// sudo sysctl -p /etc/sysctl.d/01-deprivilege-ports.conf
|
||||||
|
listener, err := net.Listen("tcp", listenAddr)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to bind %s: %v", listenAddr, err)
|
||||||
|
}
|
||||||
|
log.Printf("TCP bridge listening on %s → %s", listenAddr, target)
|
||||||
|
|
||||||
|
for {
|
||||||
|
client, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Accept error: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go handleConn(client, target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleConn(client net.Conn, target string) {
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
remote, err := net.Dial("tcp", target)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to connect to %s: %v", target, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer remote.Close()
|
||||||
|
|
||||||
|
log.Printf("New connection %s ↔ %s", client.RemoteAddr(), remote.RemoteAddr())
|
||||||
|
|
||||||
|
// Bidirectional copy with error handling
|
||||||
|
go func() { _ = copyAndClose(remote, client) }()
|
||||||
|
func() { _ = copyAndClose(client, remote) }()
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyAndClose copies until EOF or error, then closes dst
|
||||||
|
func copyAndClose(dst, src net.Conn) error {
|
||||||
|
_, err := io.Copy(dst, src)
|
||||||
|
dst.Close()
|
||||||
|
if err != nil && !isClosedConn(err) {
|
||||||
|
log.Printf("Copy error (%s → %s): %v",
|
||||||
|
src.RemoteAddr(), dst.RemoteAddr(), err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// isClosedConn detects common closed-connection errors
|
||||||
|
func isClosedConn(err error) bool {
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
opErr, ok := err.(*net.OpError)
|
||||||
|
return ok && opErr.Err.Error() == "use of closed network connection"
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user