2022-04-01 16:16:46 +00:00
|
|
|
package asnutil
|
|
|
|
|
|
|
|
import (
|
2024-06-05 20:10:03 +00:00
|
|
|
_ "embed"
|
|
|
|
"encoding/binary"
|
2022-04-01 16:16:46 +00:00
|
|
|
"errors"
|
2024-06-05 20:10:03 +00:00
|
|
|
"math"
|
2022-04-01 16:16:46 +00:00
|
|
|
"net"
|
2024-06-05 20:10:03 +00:00
|
|
|
"strconv"
|
2022-04-01 16:16:46 +00:00
|
|
|
)
|
|
|
|
|
2024-06-05 20:10:03 +00:00
|
|
|
//go:embed sorted-network-list.bin
|
|
|
|
var dataset string
|
2022-04-01 16:16:46 +00:00
|
|
|
|
2024-06-05 20:10:03 +00:00
|
|
|
const entrySize = 8*2 + 4 // start, end 8 bytes; asn 4 bytes
|
2022-04-01 16:16:46 +00:00
|
|
|
|
2024-06-05 20:10:03 +00:00
|
|
|
func readEntry(index uint) (start, end uint64, asn uint32) {
|
|
|
|
base := entrySize * index
|
|
|
|
b := dataset[base : base+entrySize]
|
|
|
|
start = uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
|
|
|
|
b = b[8:]
|
|
|
|
end = uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
|
|
|
|
b = b[8:]
|
|
|
|
asn = uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
|
|
|
|
return
|
2022-04-01 16:16:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// AsnForIPv6 returns the AS number for the given IPv6 address.
|
2024-06-05 20:10:03 +00:00
|
|
|
// If no mapping exists for the given network, this function will return a zero ASN number.
|
|
|
|
func AsnForIPv6(ip net.IP) (asn uint32) {
|
|
|
|
ip = ip.To16()
|
|
|
|
if ip == nil {
|
|
|
|
return
|
2022-04-01 16:16:46 +00:00
|
|
|
}
|
2024-06-05 20:10:03 +00:00
|
|
|
return AsnForIPv6Network(binary.BigEndian.Uint64(ip))
|
|
|
|
}
|
2022-04-01 16:16:46 +00:00
|
|
|
|
2024-06-05 20:10:03 +00:00
|
|
|
func init() {
|
|
|
|
if len(dataset) > math.MaxUint/2 {
|
|
|
|
panic("list is too big and would overflow in binary search")
|
2022-04-01 16:16:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-05 20:10:03 +00:00
|
|
|
// AsnForIPv6Network returns the AS number for the given IPv6 network.
|
|
|
|
// If no mapping exists for the given network, this function will return a zero ASN number.
|
|
|
|
// network is the first 64 bits of the ip address interpreted as big endian.
|
|
|
|
func AsnForIPv6Network(network uint64) (asn uint32) {
|
|
|
|
n := uint(len(dataset)) / entrySize
|
|
|
|
var i, j uint = 0, n
|
|
|
|
for i < j {
|
|
|
|
h := (i + j) / 2 // wont overflow since the list can't be that large
|
|
|
|
start, end, asn := readEntry(h)
|
|
|
|
if start <= network {
|
|
|
|
if network <= end {
|
|
|
|
return asn
|
|
|
|
}
|
|
|
|
i = h + 1
|
|
|
|
} else {
|
|
|
|
j = h
|
2022-04-01 16:16:46 +00:00
|
|
|
}
|
|
|
|
}
|
2024-06-05 20:10:03 +00:00
|
|
|
if i >= n {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
start, end, asn := readEntry(i)
|
|
|
|
if start <= network && network <= end {
|
|
|
|
return asn
|
|
|
|
}
|
|
|
|
return 0
|
2022-04-01 16:16:46 +00:00
|
|
|
}
|
|
|
|
|
2024-06-05 20:10:03 +00:00
|
|
|
// Deprecated: use [AsnForIPv6] or [AsnForIPv6Network], they do not allocate.
|
|
|
|
var Store backwardCompat
|
|
|
|
|
|
|
|
type backwardCompat struct{}
|
2022-04-01 16:16:46 +00:00
|
|
|
|
|
|
|
// AsnForIPv6 returns the AS number for the given IPv6 address.
|
|
|
|
// If no mapping exists for the given IP, this function will
|
|
|
|
// return an empty ASN and a nil error.
|
2024-06-05 20:10:03 +00:00
|
|
|
func (backwardCompat) AsnForIPv6(ip net.IP) (string, error) {
|
|
|
|
ip = ip.To16()
|
|
|
|
if ip == nil {
|
|
|
|
return "", errors.New("ONLY IPv6 addresses supported")
|
|
|
|
}
|
2022-04-01 16:16:46 +00:00
|
|
|
|
2024-06-05 20:10:03 +00:00
|
|
|
asn := AsnForIPv6Network(binary.BigEndian.Uint64(ip))
|
|
|
|
if asn == 0 {
|
|
|
|
return "", nil
|
2023-05-19 20:23:55 +00:00
|
|
|
}
|
2024-06-05 20:10:03 +00:00
|
|
|
return strconv.FormatUint(uint64(asn), 10), nil
|
2022-04-01 16:16:46 +00:00
|
|
|
}
|
2024-06-05 20:10:03 +00:00
|
|
|
|
|
|
|
func (backwardCompat) Init() {}
|