package proxy import ( "net" "net/http" "strings" "github.com/owen/vibeproxy/internal/runtime" ) // ClientIP returns the real client IP for r. If the immediate peer is a // trusted proxy (per snap.TrustedProxies), the right-most untrusted entry // in X-Forwarded-For is used; otherwise X-Forwarded-For is ignored // entirely and the TCP peer address is authoritative. This is the only // place XFF is trusted, and only for configured networks. func ClientIP(r *http.Request, snap *runtime.Snapshot) string { peerIP, _, err := net.SplitHostPort(r.RemoteAddr) if err != nil { peerIP = r.RemoteAddr } ip := net.ParseIP(peerIP) if ip == nil || snap == nil || !snap.IsTrustedProxy(ip) { return peerIP } xff := r.Header.Get("X-Forwarded-For") if xff == "" { return peerIP } parts := strings.Split(xff, ",") for i := len(parts) - 1; i >= 0; i-- { candidate := strings.TrimSpace(parts[i]) cip := net.ParseIP(candidate) if cip == nil { continue } if snap.IsTrustedProxy(cip) { continue // still a known proxy hop, keep walking left } return candidate } return peerIP }