mirror of
https://github.com/safing/portmaster.git
synced 2026-05-20 20:40:36 +00:00
refactor(proxy): simplify source address binding to use net.IP instead of strings
This commit is contained in:
@@ -28,13 +28,13 @@ shutdown.
|
||||
|
||||
```go
|
||||
// DeciderFunc is called once per new session to determine the upstream
|
||||
// destination and an optional local address to bind the outgoing connection to.
|
||||
// destination and an optional local IP to bind the outgoing connection to.
|
||||
// local is the proxy's listen address; peer is the connecting client's address.
|
||||
// Return a non-nil error to reject the session.
|
||||
type DeciderFunc func(local net.Addr, peer net.Addr) (
|
||||
remoteIP net.IP,
|
||||
remotePort uint16,
|
||||
localAddr string, // "host:port" to pin source address, or "" for OS default
|
||||
localIP net.IP, // source IP to pin, or nil for OS default
|
||||
extraInfo any, // optional value attached to the session's ConnContext
|
||||
err error,
|
||||
)
|
||||
@@ -87,6 +87,15 @@ func NewUDPProxyWithConfig(listenAddr string, network string, decider DeciderFun
|
||||
Both constructors bind the socket and start background goroutines immediately.
|
||||
They return an error if binding fails or if `decider` is nil.
|
||||
|
||||
### Address
|
||||
|
||||
```go
|
||||
func (p *TCPProxy) Addr() net.Addr
|
||||
func (p *UDPProxy) Addr() net.Addr
|
||||
```
|
||||
|
||||
Returns the address the proxy is currently listening on.
|
||||
|
||||
### Configuration
|
||||
|
||||
```go
|
||||
@@ -159,8 +168,8 @@ func (p *UDPProxy) Metrics() Metrics
|
||||
### Transparent TCP proxy (always route to a fixed backend)
|
||||
|
||||
```go
|
||||
decider := func(local, peer net.Addr) (net.IP, uint16, string, any, error) {
|
||||
return net.ParseIP("192.168.1.10"), 8080, "", nil, nil
|
||||
decider := func(local, peer net.Addr) (net.IP, uint16, net.IP, any, error) {
|
||||
return net.ParseIP("192.168.1.10"), 8080, nil, nil, nil
|
||||
}
|
||||
|
||||
p, err := proxy.NewTCPProxy(":8080", "tcp4", decider, nil)
|
||||
@@ -181,13 +190,13 @@ p.Shutdown(ctx)
|
||||
### Per-client routing with source-address binding (split tunnelling)
|
||||
|
||||
```go
|
||||
decider := func(local, peer net.Addr) (net.IP, uint16, string, any, error) {
|
||||
decider := func(local, peer net.Addr) (net.IP, uint16, net.IP, any, error) {
|
||||
host, _, _ := net.SplitHostPort(peer.String())
|
||||
if isTunnelledIP(host) {
|
||||
// Route through VPN interface, binding source to its local address.
|
||||
return vpnGatewayIP, 443, "10.0.0.1:0", nil, nil
|
||||
return vpnGatewayIP, 443, net.ParseIP("10.0.0.1"), nil, nil
|
||||
}
|
||||
return directGatewayIP, 443, "", nil, nil
|
||||
return directGatewayIP, 443, nil, nil, nil
|
||||
}
|
||||
|
||||
p, err := proxy.NewTCPProxy(":443", "tcp4", decider, myLogger)
|
||||
|
||||
@@ -20,10 +20,10 @@ import (
|
||||
// It returns:
|
||||
// - remoteIP: required upstream IP address
|
||||
// - remotePort: required upstream port
|
||||
// - localAddr: optional local "host:port" (empty string = OS chooses source)
|
||||
// - localIP: optional local IP to use as the source address (nil = OS chooses)
|
||||
// - extraInfo: optional user-defined object attached to the session context
|
||||
// - err: non-nil rejects the session
|
||||
type DeciderFunc func(local net.Addr, peer net.Addr) (remoteIP net.IP, remotePort uint16, localAddr string, extraInfo any, err error)
|
||||
type DeciderFunc func(local net.Addr, peer net.Addr) (remoteIP net.IP, remotePort uint16, localIP net.IP, extraInfo any, err error)
|
||||
|
||||
// Logger is the minimal structured logging interface expected by the proxies.
|
||||
// Pass nil to disable all logging.
|
||||
|
||||
@@ -16,17 +16,17 @@ import (
|
||||
// passThroughDecider always routes to dest.
|
||||
func passThroughDecider(dest string) DeciderFunc {
|
||||
addr, _ := net.ResolveTCPAddr("tcp", dest)
|
||||
return func(_, _ net.Addr) (net.IP, uint16, string, any, error) {
|
||||
return func(_, _ net.Addr) (net.IP, uint16, net.IP, any, error) {
|
||||
if addr == nil {
|
||||
return nil, 0, "", nil, fmt.Errorf("invalid dest %q", dest)
|
||||
return nil, 0, nil, nil, fmt.Errorf("invalid dest %q", dest)
|
||||
}
|
||||
return addr.IP, uint16(addr.Port), "", nil, nil
|
||||
return addr.IP, uint16(addr.Port), nil, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
// refuseDecider always rejects sessions.
|
||||
func refuseDecider(_ net.Addr, _ net.Addr) (net.IP, uint16, string, any, error) {
|
||||
return nil, 0, "", nil, fmt.Errorf("rejected")
|
||||
func refuseDecider(_ net.Addr, _ net.Addr) (net.IP, uint16, net.IP, any, error) {
|
||||
return nil, 0, nil, nil, fmt.Errorf("rejected")
|
||||
}
|
||||
|
||||
// startTCPEchoServer starts a TCP echo server on a random port.
|
||||
@@ -434,12 +434,12 @@ func TestUDPProxy_MaxSessions(t *testing.T) {
|
||||
// Count how many sessions the decider accepts; reject beyond limit.
|
||||
var accepted atomic.Int32
|
||||
const limit = 2
|
||||
decider := func(local, peer net.Addr) (net.IP, uint16, string, any, error) {
|
||||
decider := func(local, peer net.Addr) (net.IP, uint16, net.IP, any, error) {
|
||||
if accepted.Load() >= limit {
|
||||
return nil, 0, "", nil, fmt.Errorf("max sessions")
|
||||
return nil, 0, nil, nil, fmt.Errorf("max sessions")
|
||||
}
|
||||
accepted.Add(1)
|
||||
return nil, 0, "", nil, fmt.Errorf("no upstream needed for this test")
|
||||
return nil, 0, nil, nil, fmt.Errorf("no upstream needed for this test")
|
||||
}
|
||||
|
||||
cfg := DefaultConfig()
|
||||
|
||||
@@ -216,13 +216,8 @@ func (p *TCPProxy) handleConn(clientConn net.Conn) {
|
||||
|
||||
// DialContext is cancelled immediately if the proxy is shut down.
|
||||
dialer := net.Dialer{Timeout: p.cfg.DialTimeout}
|
||||
if localAddr != "" {
|
||||
tcpAddr, resolveErr := net.ResolveTCPAddr(p.network, localAddr)
|
||||
if resolveErr != nil {
|
||||
p.log.Errorf("tcp proxy: resolve local addr %q: %v", localAddr, resolveErr)
|
||||
return
|
||||
}
|
||||
dialer.LocalAddr = tcpAddr
|
||||
if localAddr != nil {
|
||||
dialer.LocalAddr = &net.TCPAddr{IP: localAddr}
|
||||
}
|
||||
upstreamConn, err := dialer.DialContext(p.shutdownCtx, p.network, destAddr)
|
||||
if err != nil {
|
||||
|
||||
@@ -235,15 +235,8 @@ func (p *UDPProxy) handlePacket(clientAddr *net.UDPAddr, data []byte) {
|
||||
|
||||
remoteAddr := &net.UDPAddr{IP: destIP, Port: int(destPort)}
|
||||
var localUDPAddr *net.UDPAddr
|
||||
if localAddr != "" {
|
||||
var resolveErr error
|
||||
localUDPAddr, resolveErr = net.ResolveUDPAddr("udp", localAddr)
|
||||
if resolveErr != nil {
|
||||
p.cache.remove(connCtx)
|
||||
cancel()
|
||||
p.log.Errorf("udp proxy: resolve local addr %q: %v", localAddr, resolveErr)
|
||||
return
|
||||
}
|
||||
if localAddr != nil {
|
||||
localUDPAddr = &net.UDPAddr{IP: localAddr}
|
||||
}
|
||||
remoteConn, err := net.DialUDP("udp", localUDPAddr, remoteAddr)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user