package server import ( "net" "net/http" "net/netip" "strings" ) // ClientIP returns the client address for access control, using X-Forwarded-For / // X-Real-IP only when the direct peer is in trusted CIDRs. func ClientIP(r *http.Request, trusted []netip.Prefix) netip.Addr { host, _, err := net.SplitHostPort(r.RemoteAddr) if err != nil { host = r.RemoteAddr } peer, err := netip.ParseAddr(host) if err != nil { return netip.Addr{} } if !containsIP(trusted, peer) { return peer } if xff := r.Header.Get("X-Forwarded-For"); xff != "" { parts := strings.Split(xff, ",") for _, p := range parts { p = strings.TrimSpace(p) if p == "" { continue } if a, err := netip.ParseAddr(p); err == nil { return a } } } if xr := strings.TrimSpace(r.Header.Get("X-Real-IP")); xr != "" { if a, err := netip.ParseAddr(xr); err == nil { return a } } return peer } func containsIP(prefixes []netip.Prefix, addr netip.Addr) bool { for _, p := range prefixes { if p.Contains(addr) { return true } } return false } // Allowed reports whether addr matches whitelist rules. func Allowed(addr netip.Addr, allowAll bool, whitelist []netip.Prefix) bool { if !addr.IsValid() { return false } if allowAll { return true } if len(whitelist) == 0 { return false } return containsIP(whitelist, addr) }