func makeHandler() http.HandlerFunc { db, err := sql.Open("sqlite3", databaseFile) if err != nil { panic(err) } mc := memcache.New(memcacheServer) return func(w http.ResponseWriter, req *http.Request) { w.Header().Set("Access-Control-Allow-Origin", "*") req.RemoteAddr = trimPort(req.RemoteAddr) // Check quota el, err := mc.Get(req.RemoteAddr) if err == memcache.ErrCacheMiss { err = mc.Set(&memcache.Item{ Key: req.RemoteAddr, Value: []byte("1"), Expiration: expirySeconds}) } if err != nil { // Service Unavailable if debug { log.Println("[debug] memcache", err.Error()) } http.Error(w, http.StatusText(503), 503) return } if el != nil { count, _ := strconv.Atoi(string(el.Value)) if count < maxRequestsPerIP { mc.Increment(req.RemoteAddr, 1) } else { // Out of quota http.Error(w, http.StatusText(403), 403) return } } Lookup(w, req, db) } }
func Lookup(w http.ResponseWriter, req *http.Request, db *sql.DB) { format, addr := req.Vars[0], req.Vars[1] if addr == "" { addr = req.RemoteAddr // port number previously removed } else { addrs, err := net.LookupHost(addr) if err != nil { http.Error(w, http.StatusText(404), 404) return } addr = addrs[0] } IP := net.ParseIP(addr) reserved := false for _, net := range reservedIPs { if net.Contains(IP) { reserved = true break } } geoip := GeoIP{Ip: addr} if reserved { geoip.CountryCode = "RD" geoip.CountryName = "Reserved" } else { q := "SELECT " + " city_location.country_code, country_blocks.country_name, " + " city_location.region_code, region_names.region_name, " + " city_location.city_name, city_location.postal_code, " + " city_location.latitude, city_location.longitude, " + " city_location.metro_code, city_location.area_code " + "FROM city_blocks " + " NATURAL JOIN city_location " + " INNER JOIN country_blocks ON " + " city_location.country_code = country_blocks.country_code " + " INNER JOIN region_names ON " + " city_location.country_code = region_names.country_code " + " AND " + " city_location.region_code = region_names.region_code " + "WHERE city_blocks.ip_start <= ? " + "ORDER BY city_blocks.ip_start DESC LIMIT 1" stmt, err := db.Prepare(q) if err != nil { if debug { log.Println("[debug] SQLite", err.Error()) } http.Error(w, http.StatusText(500), 500) return } defer stmt.Close() var uintIP uint32 b := bytes.NewBuffer(IP.To4()) binary.Read(b, binary.BigEndian, &uintIP) err = stmt.QueryRow(uintIP).Scan( &geoip.CountryCode, &geoip.CountryName, &geoip.RegionCode, &geoip.RegionName, &geoip.CityName, &geoip.ZipCode, &geoip.Latitude, &geoip.Longitude, &geoip.MetroCode, &geoip.AreaCode) if err != nil { http.Error(w, http.StatusText(404), 404) return } } switch format[0] { case 'c': w.Header().Set("Content-Type", "application/csv") fmt.Fprintf(w, `"%s","%s","%s","%s","%s","%s",`+ `"%s","%0.4f","%0.4f","%s","%s"`+"\r\n", geoip.Ip, geoip.CountryCode, geoip.CountryName, geoip.RegionCode, geoip.RegionName, geoip.CityName, geoip.ZipCode, geoip.Latitude, geoip.Longitude, geoip.MetroCode, geoip.AreaCode) case 'j': resp, err := json.Marshal(geoip) if err != nil { if debug { log.Println("[debug] JSON", err.Error()) } http.Error(w, http.StatusText(404), 404) return } callback := req.FormValue("callback") if callback != "" { w.Header().Set("Content-Type", "text/javascript") fmt.Fprintf(w, "%s(%s);\n", callback, resp) } else { w.Header().Set("Content-Type", "application/json") fmt.Fprintf(w, "%s\n", resp) } case 'x': w.Header().Set("Content-Type", "application/xml") resp, err := xml.MarshalIndent(geoip, "", " ") if err != nil { if debug { log.Println("[debug] XML", err.Error()) } http.Error(w, http.StatusText(500), 500) return } fmt.Fprintf(w, xml.Header+"%s\n", resp) } }