package main import ( "fmt" "html/template" "log" "net" "net/http" "os" "strings" "sync" "github.com/oschwald/geoip2-golang" ) var ( db *geoip2.Reader tmpl *template.Template config ServerConfig ipCache sync.Map ) type ServerConfig struct { listen string countrydb string } func main() { config = ServerConfig{ listen: getEnvOr("IP_CHECKER_LISTEN", ":8080"), countrydb: os.Getenv("IP_CHECKER_COUNTRY_DB"), } if config.countrydb != "" { var err error db, err = geoip2.Open(config.countrydb) if err != nil { log.Fatalf("Error opening GeoIP database: %v", err) } defer db.Close() } tmpl = template.Must(template.ParseFiles("assets/index.html")) http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./assets/static")))) http.HandleFunc("/{$}", handleRequest) http.HandleFunc("/robots.txt", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, `User-Agent: * Disallow: /harming/humans Disallow: /ignoring/human/orders Disallow: /harm/to/self `) }) log.Printf("Starting server on %s", config.listen) log.Fatal(http.ListenAndServe(config.listen, nil)) } func getIPCountry(ip string) string { parsedIP := net.ParseIP(ip) if parsedIP.IsPrivate() { return "private IP" } if parsedIP.IsLoopback() { return "loopback IP" } if parsedIP == nil { log.Printf("Invalid IP address: %s", ip) return "invalid IP" } if config.countrydb == "" { log.Printf("Country database not set. Returning 'unknown'") return "unknown" } if country, found := ipCache.Load(ip); found { return country.(string) } record, err := db.Country(parsedIP) if err != nil { log.Printf("GeoIP lookup failed for IP: %s, error: %v", ip, err) return "GeoIP lookup failed" } country, ok := record.Country.Names["en"] if !ok { log.Printf("Country name not found in GeoIP data for IP: %s", ip) return "unknown" } ipCache.Store(ip, country) return country } func handleRequest(w http.ResponseWriter, r *http.Request) { sourceIP := func() string { if ip := r.Header.Get("X-Forwarded-For"); ip != "" { return strings.Split(ip, ",")[0] } host, _, err := net.SplitHostPort(r.RemoteAddr) if err != nil { errorText := fmt.Sprintf("Unable to parse remote address: %v", err) log.Printf(errorText) return errorText } return host }() isHeadless := strings.HasPrefix(r.Header.Get("User-Agent"), "curl/") log.Printf("r.URL.Path: %s, sourceIP: %s, isHeadless: %t, User-Agent: %s", r.URL.Path, sourceIP, isHeadless, r.Header.Get("User-Agent")) if isHeadless { fmt.Fprintf(w, "%s\n", sourceIP) return } else { csp := []string{ "default-src 'none'", "img-src 'self'", "script-src-elem 'self'", "style-src-elem 'self' fonts.googleapis.com", "font-src fonts.gstatic.com", "connect-src api.ip.sb api-v3.speedtest.cn api.ipapi.is", } w.Header().Set("Content-Security-Policy", strings.Join(csp, "; ")) data := map[string]string{ "sourceIP": sourceIP, "sourceCountry": getIPCountry(sourceIP), } err := tmpl.Execute(w, data) if err != nil { http.Error(w, "Unable to load template", http.StatusInternalServerError) log.Printf("Template execution error: %v", err) return } } } func getEnvOr(key, defaultValue string) string { if value, exists := os.LookupEnv(key); exists { return value } return defaultValue }