README | 12 ++++++++++++ go.mod | 10 ++++++++++ go.sum | 14 ++++++++++++++ main.go | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++++ diff --git a/README b/README new file mode 100644 index 0000000000000000000000000000000000000000..f707fffc633149079c47a6656d6e6aa81e8bded5d51f38ec06af397c2893dd6b --- /dev/null +++ b/README @@ -0,0 +1,12 @@ +Print various GeoIP information for specified IP address. + + $ sgeo 2a11:e140:0:11::1 + Net: 2a11:e140::/32 + ASN: 212165 + Org: Alex Group LLC + City: Moscow + Region: Moscow + Country: RU (Russia) + Continent: EU (Europe) + Lat/Lon: 55.738600/37.744700 + Distance: 12.34 diff --git a/go.mod b/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..0bcc85c68f3bfef9df30a5f7c8243554372278071773e604a04246e279af07a3 --- /dev/null +++ b/go.mod @@ -0,0 +1,10 @@ +module go.stargrave.org/sgeo + +go 1.26.2 + +require github.com/oschwald/geoip2-golang/v2 v2.1.0 + +require ( + github.com/oschwald/maxminddb-golang/v2 v2.1.1 // indirect + golang.org/x/sys v0.38.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..43a480a6c2841732d78d6568d7f36a39859b741ed3e483ecfd4899013375dc0d --- /dev/null +++ b/go.sum @@ -0,0 +1,14 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/oschwald/geoip2-golang/v2 v2.1.0 h1:DjnLhNJu9WHwTrmoiQFvgmyJoczhdnm7LB23UBI2Amo= +github.com/oschwald/geoip2-golang/v2 v2.1.0/go.mod h1:qdVmcPgrTJ4q2eP9tHq/yldMTdp2VMr33uVdFbHBiBc= +github.com/oschwald/maxminddb-golang/v2 v2.1.1 h1:lA8FH0oOrM4u7mLvowq8IT6a3Q/qEnqRzLQn9eH5ojc= +github.com/oschwald/maxminddb-golang/v2 v2.1.1/go.mod h1:PLdx6PR+siSIoXqqy7C7r3SB3KZnhxWr1Dp6g0Hacl8= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go new file mode 100644 index 0000000000000000000000000000000000000000..e1c9336d3793c6813420cfb6a058d60b9fcdbf340a59462e0c782e8e7ba14239 --- /dev/null +++ b/main.go @@ -0,0 +1,134 @@ +package main + +import ( + "flag" + "fmt" + "log" + "math" + "net/netip" + "os" + "strconv" + "strings" + + "github.com/oschwald/geoip2-golang/v2" +) + +const ( + DegToRad = 0.017453292519943295769236907684886127134428718885417 // N[Pi/180, 50] +) + +var ( + DefaultLoc = "55.91666666,37.81666666" + MyLat, MyLon float64 +) + +func GetDistanceKm(lat1, lon1, lat2, lon2 float64) float64 { + // taken from github.com/etix/mirrorbits/utils/utils.go + R := float64(6371) // radius of the earth in Km + dLat := (lat2 - lat1) * DegToRad + dLon := (lon2 - lon1) * DegToRad + a := (math.Sin(dLat/2)* + math.Sin(dLat/2) + + math.Cos(lat1*DegToRad)* + math.Cos(lat2*DegToRad)* + math.Sin(dLon/2)* + math.Sin(dLon/2)) + c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a)) + return R * c +} + +func main() { + if s, _ := os.LookupEnv("MYLOC"); s != "" { + DefaultLoc = s + } + myLoc := flag.String("myloc", DefaultLoc, "My location") + asnsPth := flag.String("asns", "asns.mmdb", "Path to database with ASNs") + citiesPth := flag.String("cities", "cities.mmdb", "Path to database with cities") + cgi := flag.Bool("cgi", false, "Be a CGI scrit") + flag.Parse() + if *cgi { + log.SetOutput(os.Stdout) + fmt.Print("Content-Type: text/plain\n\n") + } + var err error + { + cols := strings.Split(*myLoc, ",") + if len(cols) != 2 { + log.Fatal("bad -myloc format") + } + MyLat, err = strconv.ParseFloat(cols[0], 10) + if err != nil { + log.Fatal(err) + } + MyLon, err = strconv.ParseFloat(cols[1], 10) + if err != nil { + log.Fatal(err) + } + } + var addr string + if *cgi { + addr, _ = os.LookupEnv("PATH_INFO") + addr = strings.TrimLeft(addr, "/") + } else { + if flag.NArg() == 0 { + log.Fatal("no IP asked") + } + addr = flag.Arg(0) + } + + ip, err := netip.ParseAddr(addr) + if err != nil { + log.Fatal(err) + } + asns, err := geoip2.Open(*asnsPth) + if err != nil { + log.Fatal(err) + } + cities, err := geoip2.Open(*citiesPth) + if err != nil { + log.Fatal(err) + } + { + rec, err := asns.ASN(ip) + asns.Close() + if err != nil { + log.Fatal(err) + } + if !rec.HasData() { + goto NoASN + } + fmt.Printf("Net: %v\n", rec.Network) + fmt.Printf("ASN: %v\n", rec.AutonomousSystemNumber) + fmt.Printf("Org: %v\n", rec.AutonomousSystemOrganization) + } +NoASN: + { + rec, err := cities.City(ip) + cities.Close() + if err != nil { + log.Fatal(err) + } + if !rec.HasData() { + goto NoCity + } + if len(rec.City.Names.English) > 0 { + fmt.Printf("City: %v\n", rec.City.Names.English) + } + if len(rec.Subdivisions) > 0 { + fmt.Printf("Region: %v\n", rec.Subdivisions[0].Names.English) + } + fmt.Printf("Country: %v (%v)\n", + rec.Country.ISOCode, + rec.Country.Names.English) + fmt.Printf("Continent: %v (%v)\n", + rec.Continent.Code, + rec.Continent.Names.English) + if rec.Location.Latitude != nil && rec.Location.Longitude != nil { + fmt.Printf("Lat/Lon: %f/%f\n", + *rec.Location.Latitude, *rec.Location.Longitude) + fmt.Printf("Distance: %f\n", GetDistanceKm( + MyLat, MyLon, *rec.Location.Latitude, *rec.Location.Longitude)) + } + } +NoCity: +}