From 3b61cc8d82dc9cd01fc817fa6023297aa3d66125 Mon Sep 17 00:00:00 2001 From: Yifu Yu Date: Fri, 25 Oct 2019 17:16:24 +0800 Subject: [PATCH] Initial version. --- go.mod | 5 ++ go.sum | 2 + main.go | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..7dd6c1e --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module git.eve.moe/jackyyf/navigator + +go 1.13 + +require github.com/ipipdotnet/ipdb-go v1.2.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..5a5932d --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/ipipdotnet/ipdb-go v1.2.0 h1:Afa0qx/SgRevzIK8Qg1TevuD5M28kFLWbzPvU+GQJ08= +github.com/ipipdotnet/ipdb-go v1.2.0/go.mod h1:6SFLNyXDBF6q99FQvbOZJQCc2rdPrB1V5DSy4S83RSw= diff --git a/main.go b/main.go new file mode 100644 index 0000000..d2462be --- /dev/null +++ b/main.go @@ -0,0 +1,141 @@ +package main + +import ( + "encoding/json" + "flag" + "fmt" + "log" + "net" + "net/http" + _ "net/http/pprof" + "strconv" + "strings" + + "github.com/ipipdotnet/ipdb-go" +) + +var ( + requiredFields = []string{ + "country_name", + "region_name", + "city_name", + "isp_domain", + "country_code", + "continent_code", + } +) + +const ( + errIPv4Only = "Navigator works for IPv4 only :)" +) + +type errorMessage struct { + Error string `json:error` +} + +func responseWithError(resp http.ResponseWriter, statusCode int, message string) { + resp.Header().Set("Content-Type", "text/plain") + resp.WriteHeader(statusCode) + resp.Write([]byte("error: " + message)) +} + +func responseWithJsonError(resp http.ResponseWriter, statusCode int, message string) { + resp.Header().Set("Content-Type", "application/json") + body, err := json.Marshal(&errorMessage{ + Error: message, + }) + resp.Header().Set("Content-Length", strconv.Itoa(len(body))) + if err != nil { + // This should never happen + panic("json marshal failed, check code") + } + resp.WriteHeader(statusCode) + resp.Write(body) +} + +func buildLocation(info *ipdb.CityInfo) string { + ret := "" + if info.CountryName != "" { + ret += info.CountryName + " " + } + if info.RegionName != "" { + ret += info.RegionName + " " + } + if info.CityName != "" { + ret += info.CityName + " " + } + return strings.TrimSpace(ret) +} + +func main() { + ipipdb := flag.String("ipdb-database", "ipip.db", "path to ipip database") + flag.Parse() + + db, err := ipdb.NewCity(*ipipdb) + if err != nil { + log.Fatalln("Unable to open ipdb:", err.Error()) + } + if !db.IsIPv4() { + log.Fatalln("This IPIP.net database has no IPv4 information!") + } + { + fields := db.Fields() + for _, requiredField := range requiredFields { + ok := false + for _, field := range fields { + if field == requiredField { + ok = true + break + } + } + if !ok { + log.Fatalln("This IPIP.net database has no required field", requiredField) + } + } + } + + http.HandleFunc("/healthz", func(resp http.ResponseWriter, req *http.Request) { + resp.WriteHeader(200) + resp.Write([]byte("ok")) + }) + + http.HandleFunc("/info", func(resp http.ResponseWriter, req *http.Request) { + host, _, err := net.SplitHostPort(req.RemoteAddr) + if err != nil { + responseWithError(resp, http.StatusPreconditionFailed, errIPv4Only) + return + } + if net.ParseIP(host).To4() == nil { + responseWithError(resp, http.StatusPreconditionFailed, errIPv4Only) + return + } + + info_cn, err := db.FindInfo(host, "CN") + if err != nil { + fmt.Fprintf(resp, "IP %s not found in the database.", host) + return + } + info_en, err := db.FindInfo(host, "EN") + if err != nil { + fmt.Fprintf(resp, "IP %s not found in the database.", host) + return + } + resp.Header().Set("Content-Type", "text/plain") + resp.WriteHeader(http.StatusOK) + fmt.Fprintln(resp, "您的IP:", host) + fmt.Fprintln(resp, "数据库中IP所属位置:", buildLocation(info_cn)) + if info_cn.IspDomain != "" { + fmt.Fprintln(resp, "数据库中IP所属运营商:", info_cn.IspDomain) + } + // TODO(jackyyf): add mapping info here + + fmt.Fprintln(resp, "Your IP:", host) + fmt.Fprintln(resp, "Location for your IP according our database:", buildLocation(info_en)) + if info_en.IspDomain != "" { + fmt.Fprintln(resp, "ISP for your IP according our database:", info_en.IspDomain) + } + // TODO(jackyyf): add mapping info here + + }) + http.ListenAndServe("0.0.0.0:8086", nil) +}