summaryrefslogtreecommitdiff
path: root/pkg/asn
diff options
context:
space:
mode:
authoreltrac <hi@eltr.ac>2026-11-05 11:20:32 +0800
committereltrac <hi@eltr.ac>2026-11-05 11:21:56 +0800
commit601227a20fd415ecacf39765f19cdf72acc689f4 (patch)
tree786d2fb877f8db58c1a4383f20a5a35230dd30b8 /pkg/asn
parent289790235fb0f556d87a84fc960b25ba9bf0f73e (diff)
asn: move asn/ to pkg/asn/; add lookup.goqmain
only export Lookup() and IsDatacenter()
Diffstat (limited to 'pkg/asn')
-rw-r--r--pkg/asn/lookup.go18
-rw-r--r--pkg/asn/mapping.go105
2 files changed, 123 insertions, 0 deletions
diff --git a/pkg/asn/lookup.go b/pkg/asn/lookup.go
new file mode 100644
index 0000000..4851a6f
--- /dev/null
+++ b/pkg/asn/lookup.go
@@ -0,0 +1,18 @@
+package asn
+
+var AsnTable AsnMap
+
+// Lookup return metadata of given AS
+func Lookup(ASN int) AsnEntry {
+ if len(AsnTable) == 0 {
+ AsnTable = getMapping()
+ }
+
+ return AsnTable[ASN]
+}
+
+// IsDatacenter return bool value whether an AS is datajjij center or not
+func IsDatacenter(ASN int) bool {
+ // TODO
+ return false
+}
diff --git a/pkg/asn/mapping.go b/pkg/asn/mapping.go
new file mode 100644
index 0000000..14272de
--- /dev/null
+++ b/pkg/asn/mapping.go
@@ -0,0 +1,105 @@
+package asn
+
+import (
+ "fmt"
+ "io"
+ "log"
+ "net/http"
+ "os"
+ "path/filepath"
+
+ "github.com/adrg/xdg"
+ "github.com/gocarina/gocsv"
+)
+
+const SOURCE string = "https://cdn.jsdelivr.net/gh/ipverse/as-metadata@master/as.csv"
+
+type AsnEntry struct {
+ ASN int `csv:"asn"`
+ Handle string `csv:"handle"`
+ Description string `csv:"description"`
+ CountryCode string `csv:"country-code"`
+}
+
+type AsnMap map[int]AsnEntry
+
+// localPath returns the destination of local copy of asn metadata
+func localPath() string {
+ return filepath.Join(xdg.DataHome, "/audidi/as.csv")
+}
+
+// downloadAsnSource downloads ASN metadata source to disk
+func downloadAsnSource() error {
+ resp, err := http.Get(SOURCE)
+ if err != nil {
+ return fmt.Errorf("error fetching ASN metadata: %s", err)
+ }
+ if resp.StatusCode != http.StatusOK {
+ return fmt.Errorf("error fetching ASN metadata: http %d", resp.StatusCode)
+ }
+ defer resp.Body.Close()
+
+ file, err := os.Create(localPath())
+ if err != nil {
+ return fmt.Errorf("can't open file to write ASN metadata: %s", err)
+ }
+
+ if _, err := io.Copy(file, resp.Body); err != nil {
+ return fmt.Errorf("can't copy ASN metadata to file: %s", err)
+ }
+
+ return nil
+}
+
+// useAsnSource returns either a local cached version of
+// ASN source bytes or download from source
+func useAsnSource() ([]byte, error) {
+ path := localPath()
+ os.MkdirAll(filepath.Dir(path), os.ModePerm)
+
+ if info, err := os.Stat(path); os.IsNotExist(err) || info.Size() == 0 {
+ log.Println("downloading ASN metadata...")
+ if err := downloadAsnSource(); err != nil {
+ return nil, err
+ }
+ log.Println("ASN metadata downloaded")
+ }
+
+ bytes, err := os.ReadFile(path)
+ if err != nil {
+ return nil, fmt.Errorf("can't open file to read ASN metadata: %s", err)
+ }
+
+ return bytes, nil
+}
+
+// parseMetadata parse raw bytes of ASN metadata into readable entries
+func parseMetadata(bytes []byte) ([]AsnEntry, error) {
+ var entries []AsnEntry
+
+ if err := gocsv.UnmarshalBytes(bytes, &entries); err != nil {
+ return nil, fmt.Errorf("can't parse ASN metadata: %s", err)
+ }
+
+ return entries, nil
+}
+
+// GetMapping returns a pointer to ASN-metadata mappings
+func getMapping() AsnMap {
+ bytes, err := useAsnSource()
+ if err != nil {
+ log.Fatalln("Failed to load ASN metadata: ", err)
+ }
+
+ entries, err := parseMetadata(bytes)
+ if err != nil {
+ log.Fatalln("Failed to parse ASN metadata (Delete data and retry): ", err)
+ }
+
+ mapping := make(AsnMap)
+ for _, entry := range entries {
+ mapping[entry.ASN] = entry
+ }
+
+ return mapping
+}