packagegeodbimport("context""fmt""honeypot/internal/utils""net""net/netip""strings""io""net/http""os""github.com/oschwald/maxminddb-golang/v2")// ASN minimal structtypeasnLookupstruct{AutonomousSystemNumberuint`maxminddb:"autonomous_system_number" json:"autonomous_system_number"`AutonomousSystemOrganizationstring`maxminddb:"autonomous_system_organization" json:"autonomous_system_organization"`}// Minimal "names" struct for obtaining English namestypenamesstruct{Enstring`maxminddb:"en" json:"en"`}// Only interested in city and its English name, and containing country infotypecityLookupstruct{Citystruct{Namesnames`maxminddb:"names"`}`maxminddb:"city"`Countrystruct{IsoCodestring`maxminddb:"iso_code"`Namesnames`maxminddb:"names"`}`maxminddb:"country"`Locationstruct{Latitudefloat64`maxminddb:"latitude"`Longitudefloat64`maxminddb:"longitude"`}`maxminddb:"location"`}typeIPMetadatastruct{IPstringCountrystringCountryCodestringASNintASNOrgstringCitystringLatitudefloat64Longitudefloat64FQDNstringDomainstring}typeGeoDBstruct{ASNDB*maxminddb.ReaderCityDB*maxminddb.Reader}funcNewGeoDB(asnDBPath,cityDBPathstring)(*GeoDB,error){ifasnDBPath==""||cityDBPath==""{returnnil,fmt.Errorf("database paths cannot be empty")}asnDb,err:=maxminddb.Open(asnDBPath)iferr!=nil{returnnil,fmt.Errorf("error opening ASN database: %w",err)}cityDb,err:=maxminddb.Open(cityDBPath)iferr!=nil{asnDb.Close()returnnil,fmt.Errorf("error opening city database: %w",err)}return&GeoDB{ASNDB:asnDb,CityDB:cityDb,},nil}func(g*GeoDB)Close()error{ifg.ASNDB!=nil{iferr:=g.ASNDB.Close();err!=nil{returnerr}}ifg.CityDB!=nil{iferr:=g.CityDB.Close();err!=nil{returnerr}}returnnil}func(g*GeoDB)Reload(asnDBPath,cityDBPathstring)error{newAsnDb,err:=maxminddb.Open(asnDBPath)iferr!=nil{returnfmt.Errorf("error opening ASN database: %w",err)}newCityDb,err:=maxminddb.Open(cityDBPath)iferr!=nil{newAsnDb.Close()returnfmt.Errorf("error opening city database: %w",err)}oldAsn:=g.ASNDBoldCity:=g.CityDBg.ASNDB=newAsnDbg.CityDB=newCityDbifoldAsn!=nil{err:=oldAsn.Close()iferr!=nil{returnerr}}ifoldCity!=nil{err:=oldCity.Close()iferr!=nil{returnerr}}returnnil}funcDownloadFile(url,filepathstring)error{// #nosec G107resp,err:=http.Get(url)iferr!=nil{returnerr}deferresp.Body.Close()ifresp.StatusCode!=http.StatusOK{returnfmt.Errorf("bad status: %s",resp.Status)}out,err:=os.Create(filepath)iferr!=nil{returnerr}deferout.Close()_,err=io.Copy(out,resp.Body)returnerr}func(g*GeoDB)LookupMetadata(ctxcontext.Context,ipStrstring)(*IPMetadata,error){parsedIp,err:=netip.ParseAddr(ipStr)iferr!=nil{returnnil,err}meta:=&IPMetadata{IP:ipStr,}// ASNvarasnasnLookupiferr:=g.ASNDB.Lookup(parsedIp).Decode(&asn);err==nil{meta.ASN=int(asn.AutonomousSystemNumber)meta.ASNOrg=asn.AutonomousSystemOrganization}// City/Country/LocationvarcitycityLookupiferr:=g.CityDB.Lookup(parsedIp).Decode(&city);err==nil{meta.City=city.City.Names.Enmeta.Country=city.Country.Names.Enmeta.CountryCode=city.Country.IsoCodemeta.Latitude=city.Location.Latitudemeta.Longitude=city.Location.Longitude}// Reverse DNSresolver:=&net.Resolver{}names,err:=resolver.LookupAddr(ctx,ipStr)iferr==nil&&len(names)>0{fqdn:=strings.TrimSuffix(names[0],".")meta.FQDN=fqdnmeta.Domain=utils.GetBaseDomain(fqdn)}returnmeta,nil}