| |
| package ip |
|
|
| import ( |
| "net" |
| "strings" |
|
|
| "github.com/gin-gonic/gin" |
| ) |
|
|
| |
| |
| |
| |
| |
| |
| func GetClientIP(c *gin.Context) string { |
| |
| if ip := c.GetHeader("CF-Connecting-IP"); ip != "" { |
| return normalizeIP(ip) |
| } |
|
|
| |
| if ip := c.GetHeader("X-Real-IP"); ip != "" { |
| return normalizeIP(ip) |
| } |
|
|
| |
| if xff := c.GetHeader("X-Forwarded-For"); xff != "" { |
| ips := strings.Split(xff, ",") |
| for _, ip := range ips { |
| ip = strings.TrimSpace(ip) |
| if ip != "" && !isPrivateIP(ip) { |
| return normalizeIP(ip) |
| } |
| } |
| |
| if len(ips) > 0 { |
| return normalizeIP(strings.TrimSpace(ips[0])) |
| } |
| } |
|
|
| |
| return normalizeIP(c.ClientIP()) |
| } |
|
|
| |
| |
| |
| func GetTrustedClientIP(c *gin.Context) string { |
| if c == nil { |
| return "" |
| } |
| return normalizeIP(c.ClientIP()) |
| } |
|
|
| |
| func normalizeIP(ip string) string { |
| ip = strings.TrimSpace(ip) |
| |
| if host, _, err := net.SplitHostPort(ip); err == nil { |
| return host |
| } |
| return ip |
| } |
|
|
| |
| var privateNets []*net.IPNet |
|
|
| |
| |
| type CompiledIPRules struct { |
| CIDRs []*net.IPNet |
| IPs []net.IP |
| PatternCount int |
| } |
|
|
| func init() { |
| for _, cidr := range []string{ |
| "10.0.0.0/8", |
| "172.16.0.0/12", |
| "192.168.0.0/16", |
| "127.0.0.0/8", |
| "::1/128", |
| "fc00::/7", |
| } { |
| _, block, err := net.ParseCIDR(cidr) |
| if err != nil { |
| panic("invalid CIDR: " + cidr) |
| } |
| privateNets = append(privateNets, block) |
| } |
| } |
|
|
| |
| |
| func CompileIPRules(patterns []string) *CompiledIPRules { |
| compiled := &CompiledIPRules{ |
| CIDRs: make([]*net.IPNet, 0, len(patterns)), |
| IPs: make([]net.IP, 0, len(patterns)), |
| PatternCount: len(patterns), |
| } |
| for _, pattern := range patterns { |
| normalized := strings.TrimSpace(pattern) |
| if normalized == "" { |
| continue |
| } |
| if strings.Contains(normalized, "/") { |
| _, cidr, err := net.ParseCIDR(normalized) |
| if err != nil || cidr == nil { |
| continue |
| } |
| compiled.CIDRs = append(compiled.CIDRs, cidr) |
| continue |
| } |
| parsedIP := net.ParseIP(normalized) |
| if parsedIP == nil { |
| continue |
| } |
| compiled.IPs = append(compiled.IPs, parsedIP) |
| } |
| return compiled |
| } |
|
|
| func matchesCompiledRules(parsedIP net.IP, rules *CompiledIPRules) bool { |
| if parsedIP == nil || rules == nil { |
| return false |
| } |
| for _, cidr := range rules.CIDRs { |
| if cidr.Contains(parsedIP) { |
| return true |
| } |
| } |
| for _, ruleIP := range rules.IPs { |
| if parsedIP.Equal(ruleIP) { |
| return true |
| } |
| } |
| return false |
| } |
|
|
| |
| func isPrivateIP(ipStr string) bool { |
| ip := net.ParseIP(ipStr) |
| if ip == nil { |
| return false |
| } |
| for _, block := range privateNets { |
| if block.Contains(ip) { |
| return true |
| } |
| } |
| return false |
| } |
|
|
| |
| |
| |
| |
| func MatchesPattern(clientIP, pattern string) bool { |
| ip := net.ParseIP(clientIP) |
| if ip == nil { |
| return false |
| } |
|
|
| |
| if strings.Contains(pattern, "/") { |
| _, cidr, err := net.ParseCIDR(pattern) |
| if err != nil { |
| return false |
| } |
| return cidr.Contains(ip) |
| } |
|
|
| |
| patternIP := net.ParseIP(pattern) |
| if patternIP == nil { |
| return false |
| } |
| return ip.Equal(patternIP) |
| } |
|
|
| |
| func MatchesAnyPattern(clientIP string, patterns []string) bool { |
| for _, pattern := range patterns { |
| if MatchesPattern(clientIP, pattern) { |
| return true |
| } |
| } |
| return false |
| } |
|
|
| |
| |
| |
| |
| |
| |
| func CheckIPRestriction(clientIP string, whitelist, blacklist []string) (bool, string) { |
| return CheckIPRestrictionWithCompiledRules( |
| clientIP, |
| CompileIPRules(whitelist), |
| CompileIPRules(blacklist), |
| ) |
| } |
|
|
| |
| func CheckIPRestrictionWithCompiledRules(clientIP string, whitelist, blacklist *CompiledIPRules) (bool, string) { |
| |
| clientIP = normalizeIP(clientIP) |
| if clientIP == "" { |
| return false, "access denied" |
| } |
| parsedIP := net.ParseIP(clientIP) |
| if parsedIP == nil { |
| return false, "access denied" |
| } |
|
|
| |
| if blacklist != nil && blacklist.PatternCount > 0 && matchesCompiledRules(parsedIP, blacklist) { |
| return false, "access denied" |
| } |
|
|
| |
| if whitelist != nil && whitelist.PatternCount > 0 && !matchesCompiledRules(parsedIP, whitelist) { |
| return false, "access denied" |
| } |
|
|
| return true, "" |
| } |
|
|
| |
| func ValidateIPPattern(pattern string) bool { |
| if strings.Contains(pattern, "/") { |
| _, _, err := net.ParseCIDR(pattern) |
| return err == nil |
| } |
| return net.ParseIP(pattern) != nil |
| } |
|
|
| |
| |
| func ValidateIPPatterns(patterns []string) []string { |
| var invalid []string |
| for _, p := range patterns { |
| if !ValidateIPPattern(p) { |
| invalid = append(invalid, p) |
| } |
| } |
| return invalid |
| } |
|
|