Add support for city lookup

This commit is contained in:
Martin Polden
2016-04-17 11:28:47 +02:00
parent 8027c55fdf
commit 8f71576357
7 changed files with 77 additions and 11 deletions

View File

@ -32,6 +32,7 @@ type API struct {
type Response struct {
IP net.IP `json:"ip"`
Country string `json:"country,omitempty"`
City string `json:"city,omitempty"`
Hostname string `json:"hostname,omitempty"`
}
@ -73,6 +74,10 @@ func (a *API) newResponse(r *http.Request) (Response, error) {
if err != nil {
log.Print(err)
}
city, err := a.oracle.LookupCity(ip)
if err != nil {
log.Print(err)
}
hostnames, err := a.oracle.LookupAddr(ip.String())
if err != nil {
log.Print(err)
@ -80,6 +85,7 @@ func (a *API) newResponse(r *http.Request) (Response, error) {
return Response{
IP: ip,
Country: country,
City: city,
Hostname: strings.Join(hostnames, " "),
}, nil
}
@ -102,6 +108,15 @@ func (a *API) CLICountryHandler(w http.ResponseWriter, r *http.Request) *appErro
return nil
}
func (a *API) CLICityHandler(w http.ResponseWriter, r *http.Request) *appError {
response, err := a.newResponse(r)
if err != nil {
return internalServerError(err)
}
io.WriteString(w, response.City+"\n")
return nil
}
func (a *API) JSONHandler(w http.ResponseWriter, r *http.Request) *appError {
response, err := a.newResponse(r)
if err != nil {
@ -223,6 +238,7 @@ func (a *API) Handlers() http.Handler {
r.Handle("/", appHandler(a.CLIHandler)).Methods("GET").MatcherFunc(cliMatcher)
r.Handle("/ip", appHandler(a.CLIHandler)).Methods("GET").MatcherFunc(cliMatcher)
r.Handle("/country", appHandler(a.CLICountryHandler)).Methods("GET").MatcherFunc(cliMatcher)
r.Handle("/city", appHandler(a.CLICityHandler)).Methods("GET").MatcherFunc(cliMatcher)
// Browser
r.Handle("/", appHandler(a.DefaultHandler)).Methods("GET")

View File

@ -13,9 +13,11 @@ type mockOracle struct{}
func (r *mockOracle) LookupAddr(string) ([]string, error) { return []string{"localhost"}, nil }
func (r *mockOracle) LookupCountry(net.IP) (string, error) { return "Elbonia", nil }
func (r *mockOracle) LookupCity(net.IP) (string, error) { return "Bornyasherk", nil }
func (r *mockOracle) LookupPort(net.IP, uint64) error { return nil }
func (r *mockOracle) IsLookupAddrEnabled() bool { return true }
func (r *mockOracle) IsLookupCountryEnabled() bool { return true }
func (r *mockOracle) IsLookupCityEnabled() bool { return true }
func (r *mockOracle) IsLookupPortEnabled() bool { return true }
func newTestAPI() *API {
@ -60,6 +62,7 @@ func TestClIHandlers(t *testing.T) {
{s.URL, "127.0.0.1\n", 200},
{s.URL + "/ip", "127.0.0.1\n", 200},
{s.URL + "/country", "Elbonia\n", 200},
{s.URL + "/city", "Bornyasherk\n", 200},
{s.URL + "/foo", "404 page not found", 404},
}
@ -86,7 +89,7 @@ func TestJSONHandlers(t *testing.T) {
out string
status int
}{
{s.URL, `{"ip":"127.0.0.1","country":"Elbonia","hostname":"localhost"}`, 200},
{s.URL, `{"ip":"127.0.0.1","country":"Elbonia","city":"Bornyasherk","hostname":"localhost"}`, 200},
{s.URL + "/port/31337", `{"ip":"127.0.0.1","port":31337,"reachable":true}`, 200},
{s.URL + "/foo", `{"error":"404 page not found"}`, 404},
}

View File

@ -11,18 +11,22 @@ import (
type Oracle interface {
LookupAddr(string) ([]string, error)
LookupCountry(net.IP) (string, error)
LookupCity(net.IP) (string, error)
LookupPort(net.IP, uint64) error
IsLookupAddrEnabled() bool
IsLookupCountryEnabled() bool
IsLookupCityEnabled() bool
IsLookupPortEnabled() bool
}
type DefaultOracle struct {
lookupAddr func(string) ([]string, error)
lookupCountry func(net.IP) (string, error)
lookupCity func(net.IP) (string, error)
lookupPort func(net.IP, uint64) error
lookupAddrEnabled bool
lookupCountryEnabled bool
lookupCityEnabled bool
lookupPortEnabled bool
}
@ -30,6 +34,7 @@ func NewOracle() *DefaultOracle {
return &DefaultOracle{
lookupAddr: func(string) ([]string, error) { return nil, nil },
lookupCountry: func(net.IP) (string, error) { return "", nil },
lookupCity: func(net.IP) (string, error) { return "", nil },
lookupPort: func(net.IP, uint64) error { return nil },
}
}
@ -42,6 +47,10 @@ func (r *DefaultOracle) LookupCountry(ip net.IP) (string, error) {
return r.lookupCountry(ip)
}
func (r *DefaultOracle) LookupCity(ip net.IP) (string, error) {
return r.lookupCity(ip)
}
func (r *DefaultOracle) LookupPort(ip net.IP, port uint64) error {
return r.lookupPort(ip, port)
}
@ -63,6 +72,18 @@ func (r *DefaultOracle) EnableLookupCountry(filepath string) error {
return nil
}
func (r *DefaultOracle) EnableLookupCity(filepath string) error {
db, err := geoip2.Open(filepath)
if err != nil {
return err
}
r.lookupCity = func(ip net.IP) (string, error) {
return lookupCity(db, ip)
}
r.lookupCityEnabled = true
return nil
}
func (r *DefaultOracle) EnableLookupPort() {
r.lookupPort = lookupPort
r.lookupPortEnabled = true
@ -70,6 +91,7 @@ func (r *DefaultOracle) EnableLookupPort() {
func (r *DefaultOracle) IsLookupAddrEnabled() bool { return r.lookupAddrEnabled }
func (r *DefaultOracle) IsLookupCountryEnabled() bool { return r.lookupCountryEnabled }
func (r *DefaultOracle) IsLookupCityEnabled() bool { return r.lookupCityEnabled }
func (r *DefaultOracle) IsLookupPortEnabled() bool { return r.lookupPortEnabled }
func lookupPort(ip net.IP, port uint64) error {
@ -95,3 +117,14 @@ func lookupCountry(db *geoip2.Reader, ip net.IP) (string, error) {
}
return "Unknown", fmt.Errorf("could not determine country for IP: %s", ip)
}
func lookupCity(db *geoip2.Reader, ip net.IP) (string, error) {
record, err := db.City(ip)
if err != nil {
return "", err
}
if city, exists := record.City.Names["en"]; exists {
return city, nil
}
return "Unknown", fmt.Errorf("could not determine city for IP: %s", ip)
}