From 7bf053724373e774a35c09783d457327c4ca3370 Mon Sep 17 00:00:00 2001 From: Christian Groschupp Date: Mon, 15 Apr 2024 21:41:54 +0200 Subject: [PATCH] feat: add redirect darwin support --- handler/redirect/tcp/handler_darwin.go | 64 ++++++++++++++++++++++++ handler/redirect/tcp/handler_other.go | 4 +- listener/redirect/tcp/listener_darwin.go | 9 ++++ listener/redirect/tcp/listener_other.go | 2 +- 4 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 handler/redirect/tcp/handler_darwin.go create mode 100644 listener/redirect/tcp/listener_darwin.go diff --git a/handler/redirect/tcp/handler_darwin.go b/handler/redirect/tcp/handler_darwin.go new file mode 100644 index 0000000..2e71686 --- /dev/null +++ b/handler/redirect/tcp/handler_darwin.go @@ -0,0 +1,64 @@ +package redirect + +import ( + "fmt" + "net" + "os/exec" + "strconv" + "strings" +) + +func (h *redirectHandler) getOriginalDstAddr(conn net.Conn) (addr net.Addr, err error) { + host, port, err := localToRemote(conn) + if err != nil { + return nil, err + } + portNumber, _ := strconv.Atoi(port) + addr = &net.TCPAddr{ + IP: net.ParseIP(host), + Port: portNumber, + } + + return +} + +func localToRemote(clientConn net.Conn) (string, string, error) { + host, port, err := net.SplitHostPort(clientConn.RemoteAddr().String()) + if err != nil { + return "", "", err + } + out, err := exec.Command("sudo", "-n", "/sbin/pfctl", "-s", "state").Output() + if err != nil { + return "", "", err + } + remoteAddr, remotePort, err := translatePfctlOutput(host, port, string(out)) + if err != nil { + return "", "", err + } + return remoteAddr, remotePort, err +} + +func translatePfctlOutput(address string, port, s string) (string, string, error) { + // We may get an ipv4-mapped ipv6 address here, e.g. ::ffff:127.0.0.1. + // Those still appear as "127.0.0.1" in the table, so we need to strip the prefix. + // re := regexp.MustCompile(`^::ffff:((\d+\.\d+\.\d+\.\d+$))`) + // strippedAddress := re.ReplaceAllString(address, "") + strippedAddress := address + + // ALL tcp 192.168.1.13:57474 -> 23.205.82.58:443 ESTABLISHED:ESTABLISHED + spec := net.JoinHostPort(strippedAddress, port) + + lines := strings.Split(s, "\n") + for _, line := range lines { + if strings.Contains(line, "ESTABLISHED:ESTABLISHED") { + if strings.Contains(line, spec) { + fields := strings.Fields(line) + if len(fields) > 4 { + return net.SplitHostPort(fields[4]) + } + } + } + } + + return "", "", fmt.Errorf("could not resolve original destination") +} diff --git a/handler/redirect/tcp/handler_other.go b/handler/redirect/tcp/handler_other.go index c91c633..8b09c2d 100644 --- a/handler/redirect/tcp/handler_other.go +++ b/handler/redirect/tcp/handler_other.go @@ -1,4 +1,4 @@ -//go:build !linux +//go:build !linux && !darwin package redirect @@ -7,7 +7,7 @@ import ( "net" ) -func (h *redirectHandler) getOriginalDstAddr(conn net.Conn) (addr net.Addr, err error) { +func (h *redirectHandler) getOriginalDstAddr(_ net.Conn) (addr net.Addr, err error) { err = errors.New("TCP redirect is not available on non-linux platform") return } diff --git a/listener/redirect/tcp/listener_darwin.go b/listener/redirect/tcp/listener_darwin.go new file mode 100644 index 0000000..d42f002 --- /dev/null +++ b/listener/redirect/tcp/listener_darwin.go @@ -0,0 +1,9 @@ +package tcp + +import ( + "syscall" +) + +func (l *redirectListener) control(network, address string, c syscall.RawConn) error { + return nil +} diff --git a/listener/redirect/tcp/listener_other.go b/listener/redirect/tcp/listener_other.go index c065d19..7447113 100644 --- a/listener/redirect/tcp/listener_other.go +++ b/listener/redirect/tcp/listener_other.go @@ -1,4 +1,4 @@ -//go:build !linux +//go:build !linux && !darwin package tcp