92 lines
2.0 KiB
Go
92 lines
2.0 KiB
Go
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
|
|
}
|