ip-asn/main.go
2024-12-11 19:30:02 +08:00

254 lines
6.4 KiB
Go

package main
import (
"compress/gzip"
"encoding/json"
"fmt"
"io"
"math/rand"
"net/http"
"os"
"sort"
"strings"
"time"
)
type OrgResponse struct {
Orgname string `json:"orgname"`
Source string `json:"source"`
Cached bool `json:"cached"`
}
type PrefixResponse struct {
Prefixes []struct {
Prefix string `json:"Prefix"`
Count int `json:"Count"`
Total int `json:"Total"`
} `json:"prefixes"`
}
func main() {
if len(os.Args) < 2 {
fmt.Println("Usage: go run main.go [--ipv4-only|--ipv6-only] [--minify] <AS_NUMBER1> [AS_NUMBER2 ...]")
fmt.Println("Options:")
fmt.Println(" --ipv4-only Only output IPv4 prefixes")
fmt.Println(" --ipv6-only Only output IPv6 prefixes")
fmt.Println(" --minify Output minified format to ip-asn-minified.txt")
os.Exit(1)
}
// Parse flags
var ipv4Only, ipv6Only, minify bool
var asNumbers []string
// Process arguments
for _, arg := range os.Args[1:] {
switch arg {
case "--ipv4-only":
ipv4Only = true
case "--ipv6-only":
ipv6Only = true
case "--minify":
minify = true
default:
asNumbers = append(asNumbers, arg)
}
}
if ipv4Only && ipv6Only {
fmt.Println("Error: Cannot use both --ipv4-only and --ipv6-only at the same time")
os.Exit(1)
}
if len(asNumbers) == 0 {
fmt.Println("Error: No AS numbers provided")
os.Exit(1)
}
// Create output file
outputFile, err := os.Create("ip-asn.txt")
if err != nil {
fmt.Printf("Error creating output file: %v\n", err)
return
}
defer outputFile.Close()
// Create minified output file if needed
var minifiedFile *os.File
if minify {
minifiedFile, err = os.Create("ip-asn-minified.txt")
if err != nil {
fmt.Printf("Error creating minified output file: %v\n", err)
return
}
defer minifiedFile.Close()
}
// Create HTTP client
client := &http.Client{}
// Process each AS number
for i, arg := range asNumbers {
asNumber := strings.ToLower(strings.TrimPrefix(strings.TrimPrefix(arg, "AS"), "as"))
fmt.Printf("Processing AS%s (%d/%d)...\n", asNumber, i+1, len(asNumbers))
// Random wait between requests (1-3 seconds)
if i > 0 {
waitTime := time.Duration(1000+rand.Intn(2000)) * time.Millisecond
fmt.Printf("Waiting for %v before next request...\n", waitTime)
time.Sleep(waitTime)
}
// Write AS separator and header
if i > 0 {
fmt.Fprintf(outputFile, "\n")
}
// Get organization name
fmt.Printf("Fetching organization info for AS%s...\n", asNumber)
orgReq, err := http.NewRequest("GET", fmt.Sprintf("https://bgp.he.net/orgname/as%s", asNumber), nil)
if err != nil {
fmt.Printf("Error creating request: %v\n", err)
continue
}
// Set required headers
orgReq.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0")
orgReq.Header.Set("Accept", "*/*")
orgReq.Header.Set("Accept-Language", "en-US,en;q=0.5")
orgReq.Header.Set("Accept-Encoding", "gzip")
orgReq.Header.Set("Referer", fmt.Sprintf("https://bgp.he.net/AS%s", asNumber))
orgResp, err := client.Do(orgReq)
if err != nil {
fmt.Printf("Error making request: %v\n", err)
continue
}
defer orgResp.Body.Close()
orgData, err := readResponse(orgResp)
if err != nil {
fmt.Printf("Error reading response: %v\n", err)
continue
}
var orgInfo OrgResponse
if err := json.Unmarshal(orgData, &orgInfo); err != nil {
fmt.Printf("Error parsing JSON: %v\n", err)
continue
}
fmt.Fprintf(outputFile, "# =====================================\n")
fmt.Fprintf(outputFile, "# AS%s Prefixes\n", asNumber)
fmt.Fprintf(outputFile, "# Organization: %s\n", orgInfo.Orgname)
fmt.Fprintf(outputFile, "# =====================================\n\n")
// Get prefixes
fmt.Printf("Fetching prefixes for AS%s...\n", asNumber)
prefixReq, err := http.NewRequest("GET", fmt.Sprintf("https://bgp.he.net/super-lg/report/api/v1/prefixes/originated/%s", asNumber), nil)
if err != nil {
fmt.Printf("Error creating request: %v\n", err)
continue
}
// Set the same headers for prefix request
prefixReq.Header = orgReq.Header
prefixResp, err := client.Do(prefixReq)
if err != nil {
fmt.Printf("Error making request: %v\n", err)
continue
}
defer prefixResp.Body.Close()
prefixData, err := readResponse(prefixResp)
if err != nil {
fmt.Printf("Error reading response: %v\n", err)
continue
}
var prefixInfo PrefixResponse
if err := json.Unmarshal(prefixData, &prefixInfo); err != nil {
fmt.Printf("Error parsing JSON: %v\n", err)
continue
}
// Separate IPv4 and IPv6 prefixes
var ipv4Prefixes []string
var ipv6Prefixes []string
for _, p := range prefixInfo.Prefixes {
if strings.Contains(p.Prefix, ":") {
ipv6Prefixes = append(ipv6Prefixes, p.Prefix)
} else {
ipv4Prefixes = append(ipv4Prefixes, p.Prefix)
}
}
// Sort prefixes
sort.Strings(ipv4Prefixes)
sort.Strings(ipv6Prefixes)
// Write to minified file if enabled
if minify {
if i > 0 {
fmt.Fprint(minifiedFile, " ")
}
if !ipv6Only {
for i, prefix := range ipv4Prefixes {
if i > 0 {
fmt.Fprint(minifiedFile, " ")
}
fmt.Fprint(minifiedFile, prefix)
}
}
if !ipv4Only && len(ipv6Prefixes) > 0 {
if len(ipv4Prefixes) > 0 && !ipv6Only {
fmt.Fprint(minifiedFile, " ")
}
for i, prefix := range ipv6Prefixes {
if i > 0 {
fmt.Fprint(minifiedFile, " ")
}
fmt.Fprint(minifiedFile, prefix)
}
}
}
// Write IPv4 prefixes
if len(ipv4Prefixes) > 0 && !ipv6Only {
fmt.Fprintf(outputFile, "## AS%s - %s - IPv4 Prefixes\n", asNumber, orgInfo.Orgname)
for _, prefix := range ipv4Prefixes {
fmt.Fprintln(outputFile, prefix)
}
}
// Write IPv6 prefixes
if len(ipv6Prefixes) > 0 && !ipv4Only {
if len(ipv4Prefixes) > 0 && !ipv6Only {
fmt.Fprintln(outputFile, "")
}
fmt.Fprintf(outputFile, "## AS%s - %s - IPv6 Prefixes\n", asNumber, orgInfo.Orgname)
for _, prefix := range ipv6Prefixes {
fmt.Fprintln(outputFile, prefix)
}
}
fmt.Printf("Completed processing AS%s\n", asNumber)
}
fmt.Println("All AS numbers processed successfully!")
}
func readResponse(resp *http.Response) ([]byte, error) {
var reader io.ReadCloser = resp.Body
if resp.Header.Get("Content-Encoding") == "gzip" {
var err error
reader, err = gzip.NewReader(resp.Body)
if err != nil {
return nil, fmt.Errorf("error creating gzip reader: %v", err)
}
defer reader.Close()
}
return io.ReadAll(reader)
}