diff --git a/.gitignore b/.gitignore index 5022fb8..e208308 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ /data/ /custom.html /vendor/ +.vscode/ +/bin/ \ No newline at end of file diff --git a/Makefile b/Makefile index d1da7df..87e44fd 100644 --- a/Makefile +++ b/Makefile @@ -23,13 +23,14 @@ lint: check-fmt vet install: deps go install ./... -databases := GeoLite2-City GeoLite2-Country +databases := GeoLite2-City GeoLite2-Country GeoLite2-ASN $(databases): mkdir -p data curl -fsSL -m 30 https://geolite.maxmind.com/download/geoip/database/$@.tar.gz | tar $(TAR_OPTS) --strip-components=1 -C $(CURDIR)/data -xzf - '*.mmdb' test ! -f data/GeoLite2-City.mmdb || mv data/GeoLite2-City.mmdb data/city.mmdb test ! -f data/GeoLite2-Country.mmdb || mv data/GeoLite2-Country.mmdb data/country.mmdb + test ! -f data/GeoLite2-ASN.mmdb || mv data/GeoLite2-ASN.mmdb data/asn.mmdb geoip-download: $(databases) diff --git a/README.md b/README.md index f41ec21..d1d1823 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,9 @@ EB $ curl ifconfig.co/city Bornyasherk + +$ curl ifconfig.co/asn +AS59795 ``` As JSON: @@ -48,7 +51,9 @@ $ curl -H 'Accept: application/json' ifconfig.co # or curl ifconfig.co/json "country": "Elbonia", "country_iso": "EB", "ip": "127.0.0.1", - "ip_decimal": 2130706433 + "ip_decimal": 2130706433, + "asn": "AS59795", + "asn_org": "Hosting4Real" } ``` @@ -74,7 +79,7 @@ between IPv4 and IPv6 lookup. * Supports HTTPS * Supports common command-line clients (e.g. `curl`, `httpie`, `wget` and `fetch`) * JSON output -* Country and city lookup using the MaxMind GeoIP database +* ASN, country and city lookup using the MaxMind GeoIP database * Port testing * Open source under the [BSD 3-Clause license](https://opensource.org/licenses/BSD-3-Clause) @@ -111,6 +116,7 @@ Usage: Application Options: -f, --country-db=FILE Path to GeoIP country database -c, --city-db=FILE Path to GeoIP city database + -a, --asn-db=FILE Path to GeoIP ASN database -l, --listen=ADDR Listening address (default: :8080) -r, --reverse-lookup Perform reverse hostname lookups -p, --port-lookup Enable port lookup diff --git a/cmd/echoip/main.go b/cmd/echoip/main.go index bccdeb8..9f18681 100644 --- a/cmd/echoip/main.go +++ b/cmd/echoip/main.go @@ -16,6 +16,7 @@ func main() { var opts struct { CountryDBPath string `short:"f" long:"country-db" description:"Path to GeoIP country database" value-name:"FILE" default:""` CityDBPath string `short:"c" long:"city-db" description:"Path to GeoIP city database" value-name:"FILE" default:""` + ASNDBPath string `short:"a" long:"asn-db" description:"Path to GeoIP ASN database" value-name:"FILE" default:""` Listen string `short:"l" long:"listen" description:"Listening address" value-name:"ADDR" default:":8080"` ReverseLookup bool `short:"r" long:"reverse-lookup" description:"Perform reverse hostname lookups"` PortLookup bool `short:"p" long:"port-lookup" description:"Enable port lookup"` @@ -28,7 +29,7 @@ func main() { } log := log.New(os.Stderr, "echoip: ", 0) - r, err := geo.Open(opts.CountryDBPath, opts.CityDBPath) + r, err := geo.Open(opts.CountryDBPath, opts.CityDBPath, opts.ASNDBPath) if err != nil { log.Fatal(err) } diff --git a/heroku.yml b/heroku.yml index 0d550a5..1e3ea0d 100644 --- a/heroku.yml +++ b/heroku.yml @@ -4,4 +4,4 @@ build: pre: - make geoip-download run: - web: echoip -f data/country.mmdb -c data/city.mmdb -p -r -H CF-Connecting-IP -H X-Forwarded-For -l :$PORT + web: echoip -f data/country.mmdb -c data/city.mmdb -a data/asn.mmdb -p -r -H CF-Connecting-IP -H X-Forwarded-For -l :$PORT diff --git a/http/http.go b/http/http.go index ae755c0..663effd 100644 --- a/http/http.go +++ b/http/http.go @@ -40,6 +40,8 @@ type Response struct { Hostname string `json:"hostname,omitempty"` Latitude float64 `json:"latitude,omitempty"` Longitude float64 `json:"longitude,omitempty"` + ASN string `json:"asn,omitempty"` + ASNOrg string `json:"asn_org,omitempty"` } type PortResponse struct { @@ -93,10 +95,15 @@ func (s *Server) newResponse(r *http.Request) (Response, error) { ipDecimal := iputil.ToDecimal(ip) country, _ := s.gr.Country(ip) city, _ := s.gr.City(ip) + asn, _ := s.gr.ASN(ip) var hostname string if s.LookupAddr != nil { hostname, _ = s.LookupAddr(ip) } + var autonomousSystemNumber string + if asn.AutonomousSystemNumber > 0 { + autonomousSystemNumber = fmt.Sprintf("AS%d", asn.AutonomousSystemNumber) + } return Response{ IP: ip, IPDecimal: ipDecimal, @@ -107,6 +114,8 @@ func (s *Server) newResponse(r *http.Request) (Response, error) { Hostname: hostname, Latitude: city.Latitude, Longitude: city.Longitude, + ASN: autonomousSystemNumber, + ASNOrg: asn.AutonomousSystemOrganization, }, nil } @@ -173,6 +182,15 @@ func (s *Server) CLICoordinatesHandler(w http.ResponseWriter, r *http.Request) * return nil } +func (s *Server) CLIASNHandler(w http.ResponseWriter, r *http.Request) *appError { + response, err := s.newResponse(r) + if err != nil { + return internalServerError(err) + } + fmt.Fprintf(w, "%s\n", response.ASN) + return nil +} + func (s *Server) JSONHandler(w http.ResponseWriter, r *http.Request) *appError { response, err := s.newResponse(r) if err != nil { @@ -305,6 +323,7 @@ func (s *Server) Handler() http.Handler { r.Route("GET", "/country-iso", s.CLICountryISOHandler) r.Route("GET", "/city", s.CLICityHandler) r.Route("GET", "/coordinates", s.CLICoordinatesHandler) + r.Route("GET", "/asn", s.CLIASNHandler) } // Browser diff --git a/http/http_test.go b/http/http_test.go index d7dd35f..cca5e9b 100644 --- a/http/http_test.go +++ b/http/http_test.go @@ -24,6 +24,10 @@ func (t *testDb) City(net.IP) (geo.City, error) { return geo.City{Name: "Bornyasherk", Latitude: 63.416667, Longitude: 10.416667}, nil } +func (t *testDb) ASN(net.IP) (geo.ASN, error) { + return geo.ASN{AutonomousSystemNumber: 59795, AutonomousSystemOrganization: "Hosting4Real"}, nil +} + func (t *testDb) IsEmpty() bool { return false } func testServer() *Server { @@ -70,6 +74,7 @@ func TestCLIHandlers(t *testing.T) { {s.URL + "/coordinates", "63.416667,10.416667\n", 200, "", ""}, {s.URL + "/city", "Bornyasherk\n", 200, "", ""}, {s.URL + "/foo", "404 page not found", 404, "", ""}, + {s.URL + "/asn", "AS59795\n", 200, "", ""}, } for _, tt := range tests { @@ -91,7 +96,7 @@ func TestDisabledHandlers(t *testing.T) { server := testServer() server.LookupPort = nil server.LookupAddr = nil - server.gr, _ = geo.Open("", "") + server.gr, _ = geo.Open("", "", "") s := httptest.NewServer(server.Handler()) var tests = []struct { @@ -129,7 +134,7 @@ func TestJSONHandlers(t *testing.T) { out string status int }{ - {s.URL, `{"ip":"127.0.0.1","ip_decimal":2130706433,"country":"Elbonia","country_eu":false,"country_iso":"EB","city":"Bornyasherk","hostname":"localhost","latitude":63.416667,"longitude":10.416667}`, 200}, + {s.URL, `{"ip":"127.0.0.1","ip_decimal":2130706433,"country":"Elbonia","country_eu":false,"country_iso":"EB","city":"Bornyasherk","hostname":"localhost","latitude":63.416667,"longitude":10.416667,"asn":"AS59795","asn_org":"Hosting4Real"}`, 200}, {s.URL + "/port/foo", `{"error":"invalid port: foo"}`, 400}, {s.URL + "/port/0", `{"error":"invalid port: 0"}`, 400}, {s.URL + "/port/65537", `{"error":"invalid port: 65537"}`, 400}, diff --git a/index.html b/index.html index 430964e..e988e7e 100644 --- a/index.html +++ b/index.html @@ -77,6 +77,15 @@ $ http {{ .Host }}/country-iso
 $ http {{ .Host }}/city
 {{ .City }}
+{{ end }} +{{ if .ASN }} +

ASN lookup

+
+$ http ip.alphakilo.eu/asn
+{{ .ASN }}
+{{ if .ASNOrg }}
+

Looks like you're with {{ .ASNOrg }}

+{{ end }} {{ end }}
@@ -131,7 +140,7 @@ $ http {{ .Host }}/port/8080
- {{ if or .Country .City }} + {{ if or .Country .City .ASN .ASNOrg }}