package main import ( "fmt" "net" "net/http" "strings" ) type Service struct { Name string Subdomains map[string]bool Directories map[string]bool FilterV4 bool FilterV6 bool } func BuildServices(cfg *Config) map[string]*Service { services := make(map[string]*Service) for name, sc := range cfg.Services { if !sc.Enabled { continue } svc := &Service{ Name: name, Subdomains: make(map[string]bool), Directories: make(map[string]bool), } for _, s := range sc.Subdomains { svc.Subdomains[strings.ToLower(s)] = true } for _, d := range sc.Directories { svc.Directories[d] = true } switch name { case "ip4": svc.FilterV4 = true case "ip6": svc.FilterV6 = true } services[name] = svc } return services } func extractIP(r *http.Request) net.IP { if xff := r.Header.Get("X-Forwarded-For"); xff != "" { parts := strings.Split(xff, ",") if ip := net.ParseIP(strings.TrimSpace(parts[0])); ip != nil { return ip } } if xri := r.Header.Get("X-Real-IP"); xri != "" { if ip := net.ParseIP(strings.TrimSpace(xri)); ip != nil { return ip } } host, _, err := net.SplitHostPort(r.RemoteAddr) if err != nil { return net.ParseIP(r.RemoteAddr) } return net.ParseIP(host) } func matchService(host, path string, services map[string]*Service) *Service { hostname := host if i := strings.IndexByte(host, ':'); i != -1 { hostname = host[:i] } for _, svc := range services { if svc.Subdomains[hostname] { return svc } } parts := strings.Split(hostname, ".") if len(parts) >= 2 { label := parts[len(parts)-2] for _, svc := range services { if svc.Subdomains[label] { return svc } } } for _, svc := range services { if svc.Directories[path] { return svc } } return nil } func Handle(services map[string]*Service, geo *Geo) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { svc := matchService(r.Host, r.URL.Path, services) if svc == nil { http.NotFound(w, r) return } ip := extractIP(r) if ip == nil { http.Error(w, "cannot determine client IP", 500) return } isV4 := ip.To4() != nil if (svc.FilterV4 && !isV4) || (svc.FilterV6 && isV4) { http.Error(w, fmt.Sprintf("this endpoint requires %s", map[bool]string{true: "IPv6", false: "IPv4"}[svc.FilterV6]), 400) return } info := geo.Lookup(ip) fmt.Fprintf(w, "%s\n", ip) fmt.Fprintf(w, "%s\n", info.ReverseDNS) fmt.Fprintf(w, "proxy=NO, type=UNKNOWN, tor=NO\n") continent, country, city := info.Continent, info.Country, info.City if continent == "" { continent = "Unknown" } if country == "" { country = "Unknown" } if city == "" { city = "Unknown" } fmt.Fprintf(w, "%s, %s, %s\n", continent, country, city) if info.Org != "" { fmt.Fprintf(w, "%s / %s\n", info.Org, info.ASN) } else { fmt.Fprintf(w, "Unknown\n") } } }