Make geo database usage optional (#39)

This commit is contained in:
2025-01-02 20:13:41 +01:00
committed by GitHub
parent 95e7742c56
commit b5fe362183
24 changed files with 457 additions and 268 deletions

View File

@ -7,7 +7,6 @@ import (
"strings"
validator "github.com/dcarrillo/whatismyip/internal/validator/uuid"
"github.com/dcarrillo/whatismyip/service"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/patrickmn/go-cache"
@ -16,10 +15,14 @@ import (
type DNSJSONResponse struct {
DNS dnsData `json:"dns"`
}
type dnsGeoData struct {
Country string `json:"country,omitempty"`
AsnOrganization string `json:"provider,omitempty"`
}
type dnsData struct {
IP string `json:"ip"`
Country string `json:"country"`
AsnOrganization string `json:"provider"`
IP string `json:"ip"`
dnsGeoData
}
// TODO
@ -67,12 +70,21 @@ func handleDNS(ctx *gin.Context, store *cache.Cache) {
return
}
geo := service.Geo{IP: ip}
geoResp := dnsGeoData{}
if geoSvc != nil {
cityRecord := geoSvc.LookUpCity(ip)
asnRecord := geoSvc.LookUpASN(ip)
geoResp = dnsGeoData{
Country: cityRecord.Country.Names["en"],
AsnOrganization: asnRecord.AutonomousSystemOrganization,
}
}
j := DNSJSONResponse{
DNS: dnsData{
IP: ipStr,
Country: geo.LookUpCity().Country.Names["en"],
AsnOrganization: geo.LookUpASN().AutonomousSystemOrganization,
IP: ipStr,
dnsGeoData: geoResp,
},
}

View File

@ -7,25 +7,28 @@ import (
"github.com/dcarrillo/whatismyip/internal/httputils"
"github.com/dcarrillo/whatismyip/internal/setting"
"github.com/dcarrillo/whatismyip/service"
"github.com/gin-gonic/gin"
)
type GeoResponse struct {
Country string `json:"country,omitempty"`
CountryCode string `json:"country_code,omitempty"`
City string `json:"city,omitempty"`
Latitude float64 `json:"latitude,omitempty"`
Longitude float64 `json:"longitude,omitempty"`
PostalCode string `json:"postal_code,omitempty"`
TimeZone string `json:"time_zone,omitempty"`
ASN uint `json:"asn,omitempty"`
ASNOrganization string `json:"asn_organization,omitempty"`
}
type JSONResponse struct {
IP string `json:"ip"`
IPVersion byte `json:"ip_version"`
ClientPort string `json:"client_port"`
Country string `json:"country"`
CountryCode string `json:"country_code"`
City string `json:"city"`
Latitude float64 `json:"latitude"`
Longitude float64 `json:"longitude"`
PostalCode string `json:"postal_code"`
TimeZone string `json:"time_zone"`
ASN uint `json:"asn"`
ASNOrganization string `json:"asn_organization"`
Host string `json:"host"`
Headers http.Header `json:"headers"`
IP string `json:"ip"`
IPVersion byte `json:"ip_version"`
ClientPort string `json:"client_port"`
Host string `json:"host"`
Headers http.Header `json:"headers"`
GeoResponse
}
func getRoot(ctx *gin.Context) {
@ -66,16 +69,14 @@ func getClientPortAsString(ctx *gin.Context) {
}
func getAllAsString(ctx *gin.Context) {
output := "IP: " + ctx.ClientIP() + "\n"
ip := net.ParseIP(ctx.ClientIP())
output := "IP: " + ip.String() + "\n"
output += "Client Port: " + getClientPort(ctx) + "\n"
r := service.Geo{IP: net.ParseIP(ctx.ClientIP())}
if record := r.LookUpCity(); record != nil {
output += geoCityRecordToString(record) + "\n"
}
if record := r.LookUpASN(); record != nil {
output += geoASNRecordToString(record) + "\n"
if geoSvc != nil {
output += geoCityRecordToString(geoSvc.LookUpCity(ip)) + "\n"
output += geoASNRecordToString(geoSvc.LookUpASN(ip)) + "\n"
}
h := httputils.GetHeadersWithoutTrustedHeaders(ctx)
@ -90,28 +91,37 @@ func getJSON(ctx *gin.Context) {
}
func jsonOutput(ctx *gin.Context) JSONResponse {
ip := service.Geo{IP: net.ParseIP(ctx.ClientIP())}
asnRecord := ip.LookUpASN()
cityRecord := ip.LookUpCity()
ip := net.ParseIP(ctx.ClientIP())
var version byte = 4
if p := net.ParseIP(ctx.ClientIP()).To4(); p == nil {
if p := ip.To4(); p == nil {
version = 6
}
geoResp := GeoResponse{}
if geoSvc != nil {
cityRecord := geoSvc.LookUpCity(ip)
asnRecord := geoSvc.LookUpASN(ip)
geoResp = GeoResponse{
Country: cityRecord.Country.Names["en"],
CountryCode: cityRecord.Country.ISOCode,
City: cityRecord.City.Names["en"],
Latitude: cityRecord.Location.Latitude,
Longitude: cityRecord.Location.Longitude,
PostalCode: cityRecord.Postal.Code,
TimeZone: cityRecord.Location.TimeZone,
ASN: asnRecord.AutonomousSystemNumber,
ASNOrganization: asnRecord.AutonomousSystemOrganization,
}
}
return JSONResponse{
IP: ctx.ClientIP(),
IPVersion: version,
ClientPort: getClientPort(ctx),
Country: cityRecord.Country.Names["en"],
CountryCode: cityRecord.Country.ISOCode,
City: cityRecord.City.Names["en"],
Latitude: cityRecord.Location.Latitude,
Longitude: cityRecord.Location.Longitude,
PostalCode: cityRecord.Postal.Code,
TimeZone: cityRecord.Location.TimeZone,
ASN: asnRecord.AutonomousSystemNumber,
ASNOrganization: asnRecord.AutonomousSystemOrganization,
Host: ctx.Request.Host,
Headers: httputils.GetHeadersWithoutTrustedHeaders(ctx),
IP: ip.String(),
IPVersion: version,
ClientPort: getClientPort(ctx),
Host: ctx.Request.Host,
Headers: httputils.GetHeadersWithoutTrustedHeaders(ctx),
GeoResponse: geoResp,
}
}

View File

@ -8,7 +8,6 @@ import (
"strings"
"github.com/dcarrillo/whatismyip/models"
"github.com/dcarrillo/whatismyip/service"
"github.com/gin-gonic/gin"
)
@ -83,10 +82,13 @@ var asnOutput = map[string]asnDataFormatter{
}
func getGeoAsString(ctx *gin.Context) {
field := strings.ToLower(ctx.Params.ByName("field"))
ip := service.Geo{IP: net.ParseIP(ctx.ClientIP())}
record := ip.LookUpCity()
if geoSvc == nil {
ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))
return
}
field := strings.ToLower(ctx.Params.ByName("field"))
record := geoSvc.LookUpCity(net.ParseIP(ctx.ClientIP()))
if field == "" {
ctx.String(http.StatusOK, geoCityRecordToString(record))
} else if g, ok := geoOutput[field]; ok {
@ -97,10 +99,12 @@ func getGeoAsString(ctx *gin.Context) {
}
func getASNAsString(ctx *gin.Context) {
if geoSvc == nil {
ctx.String(http.StatusNotFound, http.StatusText(http.StatusNotFound))
return
}
field := strings.ToLower(ctx.Params.ByName("field"))
ip := service.Geo{IP: net.ParseIP(ctx.ClientIP())}
record := ip.LookUpASN()
record := geoSvc.LookUpASN(net.ParseIP(ctx.ClientIP()))
if field == "" {
ctx.String(http.StatusOK, geoASNRecordToString(record))
} else if g, ok := asnOutput[field]; ok {

View File

@ -5,9 +5,12 @@ import (
"log"
"github.com/dcarrillo/whatismyip/internal/setting"
"github.com/dcarrillo/whatismyip/service"
"github.com/gin-gonic/gin"
)
var geoSvc *service.Geo
func SetupTemplate(r *gin.Engine) {
if setting.App.TemplatePath == "" {
t, _ := template.New("home").Parse(home)
@ -18,7 +21,8 @@ func SetupTemplate(r *gin.Engine) {
}
}
func Setup(r *gin.Engine) {
func Setup(r *gin.Engine, geo *service.Geo) {
geoSvc = geo
r.GET("/", getRoot)
r.GET("/scan/tcp/:port", scanTCPPort)
r.GET("/client-port", getClientPortAsString)

View File

@ -1,10 +1,11 @@
package router
import (
"context"
"os"
"testing"
"github.com/dcarrillo/whatismyip/models"
"github.com/dcarrillo/whatismyip/service"
"github.com/gin-gonic/gin"
)
@ -34,9 +35,9 @@ var (
text: "text/plain; charset=utf-8",
json: "application/json; charset=utf-8",
}
jsonIPv4 = `{"client_port":"1001","ip":"81.2.69.192","ip_version":4,"country":"United Kingdom","country_code":"GB","city":"London","latitude":51.5142,"longitude":-0.0931,"postal_code":"","time_zone":"Europe/London","asn":0,"asn_organization":"","host":"test", "headers": {}}`
jsonIPv6 = `{"asn":3352, "asn_organization":"TELEFONICA DE ESPANA", "city":"", "client_port":"1001", "country":"", "country_code":"", "host":"test", "ip":"2a02:9000::1", "ip_version":6, "latitude":0, "longitude":0, "postal_code":"", "time_zone":"", "headers": {}}`
jsonDNSIPv4 = `{"dns":{"ip":"81.2.69.192","country":"United Kingdom","provider":""}}`
jsonIPv4 = `{"client_port":"1001","ip":"81.2.69.192","ip_version":4,"country":"United Kingdom","country_code":"GB","city":"London","latitude":51.5142,"longitude":-0.0931,"time_zone":"Europe/London","host":"test", "headers": {}}`
jsonIPv6 = `{"asn":3352,"asn_organization":"TELEFONICA DE ESPANA","client_port":"1001","host":"test","ip":"2a02:9000::1","ip_version":6,"headers": {}}`
jsonDNSIPv4 = `{"dns":{"ip":"81.2.69.192","country":"United Kingdom"}}`
plainDNSIPv4 = "81.2.69.192 (United Kingdom / )\n"
)
@ -49,9 +50,8 @@ const (
func TestMain(m *testing.M) {
app = gin.Default()
app.TrustedPlatform = trustedHeader
models.Setup("../test/GeoIP2-City-Test.mmdb", "../test/GeoLite2-ASN-Test.mmdb")
Setup(app)
defer models.CloseDBs()
svc, _ := service.NewGeo(context.Background(), "../test/GeoIP2-City-Test.mmdb", "../test/GeoLite2-ASN-Test.mmdb")
Setup(app, svc)
os.Exit(m.Run())
}

View File

@ -62,20 +62,22 @@ func TestDefaultTemplate(t *testing.T) {
tmpl, _ := template.New("home").Parse(home)
response := JSONResponse{
IP: "127.0.0.1",
IPVersion: 4,
ClientPort: "1000",
Country: "A Country",
CountryCode: "XX",
City: "A City",
Latitude: 100,
Longitude: -100,
PostalCode: "00000",
TimeZone: "My/Timezone",
ASN: 0,
ASNOrganization: "My ISP",
Host: "localhost",
Headers: req.Header,
IP: "127.0.0.1",
IPVersion: 4,
ClientPort: "1000",
Host: "localhost",
Headers: req.Header,
GeoResponse: GeoResponse{
Country: "A Country",
CountryCode: "XX",
City: "A City",
Latitude: 100,
Longitude: -100,
PostalCode: "00000",
TimeZone: "My/Timezone",
ASN: 0,
ASNOrganization: "My ISP",
},
}
buf := &bytes.Buffer{}