package httpx import ( "net" "net/http" "strings" ) // ClientIP 按 DEVELOPMENT 4.5:仅当对端在 trusted 网段内时采信 // X-Forwarded-For(从右往左第一个不在 trusted 内的地址)。 func ClientIP(r *http.Request, trusted []*net.IPNet) string { host, _, err := net.SplitHostPort(r.RemoteAddr) if err != nil { host = r.RemoteAddr } ip := net.ParseIP(host) if ip == nil { return host } if !ipInNets(ip, trusted) { return ip.String() } xff := r.Header.Get("X-Forwarded-For") if xff == "" { return ip.String() } parts := strings.Split(xff, ",") for i := len(parts) - 1; i >= 0; i-- { cand := strings.TrimSpace(parts[i]) parsed := net.ParseIP(cand) if parsed == nil { continue } if !ipInNets(parsed, trusted) { return parsed.String() } } return ip.String() } // IsHTTPS 判定请求是否视为 HTTPS(直连 TLS 或受信任代理的 X-Forwarded-Proto)。 func IsHTTPS(r *http.Request, trusted []*net.IPNet) bool { if r.TLS != nil { return true } host, _, err := net.SplitHostPort(r.RemoteAddr) if err != nil { host = r.RemoteAddr } ip := net.ParseIP(host) if ip == nil || !ipInNets(ip, trusted) { return false } proto := strings.ToLower(strings.TrimSpace(r.Header.Get("X-Forwarded-Proto"))) return proto == "https" } // ParseCIDRs 解析 CIDR 列表;非法项跳过。 func ParseCIDRs(cidrs []string) []*net.IPNet { var out []*net.IPNet for _, c := range cidrs { c = strings.TrimSpace(c) if c == "" { continue } _, n, err := net.ParseCIDR(c) if err != nil { // 允许单 IP 写成无掩码 if ip := net.ParseIP(c); ip != nil { if ip.To4() != nil { _, n, err = net.ParseCIDR(ip.String() + "/32") } else { _, n, err = net.ParseCIDR(ip.String() + "/128") } } } if err == nil && n != nil { out = append(out, n) } } return out } func ipInNets(ip net.IP, nets []*net.IPNet) bool { for _, n := range nets { if n.Contains(ip) { return true } } return false }