You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
277 lines
5.4 KiB
277 lines
5.4 KiB
package main |
|
|
|
import ( |
|
_ "embed" |
|
"flag" |
|
"fmt" |
|
"log" |
|
"net" |
|
"strconv" |
|
"strings" |
|
"time" |
|
|
|
"io/ioutil" |
|
"net/http" |
|
"net/url" |
|
|
|
"encoding/json" |
|
|
|
"github.com/aeden/traceroute" |
|
"github.com/oschwald/maxminddb-golang" |
|
) |
|
|
|
var isFinish bool = false |
|
var destIP net.IP |
|
|
|
func remove(slice []string, s int) []string { |
|
if s < len(slice) { |
|
return append(slice[:s], slice[s+1:]...) |
|
} |
|
return slice |
|
} |
|
|
|
func removeEmpty(slice []string) (ret []string) { |
|
for i := 0; i < len(slice); i++ { |
|
if (len(slice[i])) != 0 { |
|
ret = append(ret, slice[i]) |
|
} |
|
} |
|
return |
|
} |
|
|
|
type record struct { |
|
ASN int `maxminddb:"autonomous_system_number"` |
|
ASO string `maxminddb:"autonomous_system_organization"` |
|
} |
|
|
|
type IPDetail struct { |
|
Country string `json:"country"` |
|
Province string `json:"province"` |
|
City string `json:"city"` |
|
ISP string `json:"isp"` |
|
} |
|
|
|
type APIResponse struct { |
|
Code int `json:"code"` |
|
Detail IPDetail `json:"data"` |
|
} |
|
|
|
//go:embed `GeoLite2-ASN.mmdb` |
|
var buffer []byte |
|
|
|
func parseIPFromMap42(ip net.IP) (record, APIResponse, error) { |
|
params := url.Values{} |
|
|
|
URL, err := url.Parse("http://ipip.map.dn42/whois") |
|
|
|
if err != nil { |
|
return record{}, APIResponse{}, err |
|
} |
|
|
|
params.Set("ip", ip.String()) |
|
params.Set("lang", "cn") |
|
|
|
URL.RawQuery = params.Encode() |
|
|
|
resp, err := http.Get(URL.String()) |
|
|
|
if err != nil { |
|
return record{}, APIResponse{}, err |
|
} |
|
|
|
defer resp.Body.Close() |
|
|
|
body, _ := ioutil.ReadAll(resp.Body) |
|
var res struct { |
|
ASN string `json:"as"` |
|
Area string `json:"area"` |
|
} |
|
_ = json.Unmarshal(body, &res) |
|
|
|
ASN, err := strconv.Atoi(strings.Replace(res.ASN, "AS", "", -1)) |
|
if err != nil { |
|
return record{}, APIResponse{}, err |
|
} |
|
|
|
record := record{ |
|
ASN: ASN, |
|
} |
|
|
|
APIResponse := APIResponse{ |
|
Code: 0, |
|
Detail: IPDetail{ |
|
ISP: strings.Join(removeEmpty(remove(remove(strings.Split(res.Area, "\t"), 6), 5)), " "), |
|
}, |
|
} |
|
return record, APIResponse, nil |
|
} |
|
|
|
func parseIPFromMaxminddb(ip net.IP) record { |
|
db, err := maxminddb.FromBytes(buffer) |
|
if err != nil { |
|
log.Fatal(err) |
|
} |
|
defer db.Close() |
|
|
|
var record record |
|
|
|
err = db.Lookup(ip, &record) |
|
|
|
if err != nil { |
|
log.Fatal(err) |
|
} |
|
|
|
return record |
|
} |
|
|
|
func parseIPFromBilibiliAPI(ip net.IP) APIResponse { |
|
params := url.Values{} |
|
|
|
URL, err := url.Parse("https://api.live.bilibili.com/client/v1/Ip/getInfoNew") |
|
|
|
if err != nil { |
|
log.Fatal(err) |
|
} |
|
|
|
params.Set("ip", ip.String()) |
|
|
|
URL.RawQuery = params.Encode() |
|
|
|
resp, err := http.Get(URL.String()) |
|
|
|
if err != nil { |
|
log.Fatal(err) |
|
} |
|
|
|
defer resp.Body.Close() |
|
|
|
body, _ := ioutil.ReadAll(resp.Body) |
|
var res APIResponse |
|
_ = json.Unmarshal(body, &res) |
|
return res |
|
} |
|
|
|
func printHop(hop traceroute.TracerouteHop) { |
|
addr := address(hop.Address) |
|
ip := net.ParseIP(addr) |
|
|
|
var info record |
|
var APIResponse APIResponse |
|
var err error |
|
|
|
if _, Prefix, _ := net.ParseCIDR("172.16.0.0/12"); Prefix.Contains(ip) { |
|
info, APIResponse, err = parseIPFromMap42(ip) |
|
if err != nil { |
|
info = parseIPFromMaxminddb(ip) |
|
APIResponse = parseIPFromBilibiliAPI(ip) |
|
} |
|
} else { |
|
info = parseIPFromMaxminddb(ip) |
|
APIResponse = parseIPFromBilibiliAPI(ip) |
|
} |
|
|
|
hostOrAddr := addr |
|
|
|
if hop.Host != "" { |
|
hostOrAddr = hop.Host |
|
} |
|
|
|
var ASN string |
|
|
|
if hop.Success { |
|
if info.ASN == 0 { |
|
ASN = "unknown" |
|
} else { |
|
ASN = fmt.Sprintf("AS%v", info.ASN) |
|
} |
|
if APIResponse.Code != 0 { |
|
fmt.Printf("%-3d %8v %15v %18v %8v\n", hop.TTL, ASN, hostOrAddr, "("+addr+")", hop.ElapsedTime.Round(time.Microsecond)) |
|
} else { |
|
fmt.Printf("%-3d %8v %15v %18v %8v ", hop.TTL, ASN, hostOrAddr, "("+addr+")", hop.ElapsedTime.Round(time.Microsecond)) |
|
|
|
var flag bool = false |
|
|
|
if APIResponse.Detail.Country != "" { |
|
flag = true |
|
fmt.Printf(APIResponse.Detail.Country) |
|
} |
|
if APIResponse.Detail.Province != "" { |
|
if flag { |
|
fmt.Printf(", ") |
|
} |
|
fmt.Print(APIResponse.Detail.Province) |
|
flag = true |
|
} |
|
if APIResponse.Detail.City != "" { |
|
if flag { |
|
fmt.Printf(", ") |
|
} |
|
fmt.Print(APIResponse.Detail.City) |
|
flag = true |
|
} |
|
|
|
if APIResponse.Detail.ISP != "" { |
|
if flag { |
|
fmt.Print(" ") |
|
} |
|
fmt.Print(APIResponse.Detail.ISP) |
|
flag = true |
|
} |
|
|
|
fmt.Printf("\n") |
|
} |
|
|
|
} else { |
|
fmt.Printf("%-3d *\n", hop.TTL) |
|
} |
|
|
|
if destIP.Equal(ip) { |
|
isFinish = true |
|
} |
|
|
|
} |
|
|
|
func address(address [4]byte) string { |
|
return fmt.Sprintf("%v.%v.%v.%v", address[0], address[1], address[2], address[3]) |
|
} |
|
|
|
func main() { |
|
var m = flag.Int("m", traceroute.DEFAULT_MAX_HOPS, `Set the max time-to-live (max number of hops) used in outgoing probe packets (default is 64)`) |
|
var f = flag.Int("f", traceroute.DEFAULT_FIRST_HOP, `Set the first used time-to-live, e.g. the first hop (default is 1)`) |
|
var q = flag.Int("q", 1, `Set the number of probes per "ttl" to nqueries (default is one probe).`) |
|
|
|
flag.Parse() |
|
host := flag.Arg(0) |
|
options := traceroute.TracerouteOptions{} |
|
options.SetRetries(*q - 1) |
|
options.SetMaxHops(*m) |
|
options.SetFirstHop(*f) |
|
|
|
ipAddr, err := net.ResolveIPAddr("ip", host) |
|
if err != nil { |
|
return |
|
} |
|
|
|
fmt.Printf("traceroute to %v (%v), %v hops max, %v byte packets, using ICMP methods.\n", host, ipAddr, options.MaxHops(), options.PacketSize()) |
|
|
|
c := make(chan traceroute.TracerouteHop, 0) |
|
d := make(chan bool) |
|
go func() { |
|
for { |
|
hop, ok := <-c |
|
if !ok { |
|
fmt.Println() |
|
return |
|
} |
|
printHop(hop) |
|
d <- true |
|
} |
|
}() |
|
|
|
destIP = ipAddr.IP |
|
|
|
_, err = traceroute.Traceroute(ipAddr.String(), &options, d, c) |
|
if err != nil { |
|
fmt.Printf("Error: %v", err) |
|
} |
|
}
|
|
|