Initial version.
This commit is contained in:
		
							
								
								
									
										5
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | module git.eve.moe/jackyyf/navigator | ||||||
|  |  | ||||||
|  | go 1.13 | ||||||
|  |  | ||||||
|  | require github.com/ipipdotnet/ipdb-go v1.2.0 | ||||||
							
								
								
									
										2
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							| @ -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= | ||||||
							
								
								
									
										141
									
								
								main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								main.go
									
									
									
									
									
										Normal file
									
								
							| @ -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) | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user