mirror of
https://github.com/oneclickvirt/backtrace.git
synced 2026-07-23 11:20:13 +08:00
81 lines
2.8 KiB
Go
81 lines
2.8 KiB
Go
package backtrace
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
)
|
|
|
|
const defaultPrefixListLimit = 1 << 16
|
|
|
|
var (
|
|
ErrInvalidPrefix = errors.New("invalid IPv4 prefix")
|
|
ErrIPv6PrefixUnsupported = errors.New("IPv6 prefix generation is unsupported")
|
|
ErrPrefixListTooLarge = errors.New("prefix list exceeds limit")
|
|
)
|
|
|
|
// GeneratePrefixList generates the legacy list of /24 address prefixes. The
|
|
// old API cannot return an error, so invalid, IPv6, and over-sized inputs
|
|
// return nil. Callers that need to distinguish these cases should use
|
|
// GeneratePrefixListWithLimit.
|
|
func GeneratePrefixList(prefix string) []string {
|
|
result, err := GeneratePrefixListWithLimit(prefix, defaultPrefixListLimit)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
return result
|
|
}
|
|
|
|
// GeneratePrefixListWithLimit generates one entry for every /24-sized block
|
|
// covered by an IPv4 CIDR. Prefixes narrower than /24 are rejected once the
|
|
// number of blocks exceeds maxEntries; this keeps /0 and similarly broad
|
|
// input from allocating or iterating unbounded data. A prefix longer than
|
|
// /24 still produces the containing block, preserving the legacy behaviour.
|
|
func GeneratePrefixListWithLimit(prefix string, maxEntries int) ([]string, error) {
|
|
if maxEntries <= 0 {
|
|
maxEntries = defaultPrefixListLimit
|
|
}
|
|
_, ipNet, err := net.ParseCIDR(prefix)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: %q", ErrInvalidPrefix, prefix)
|
|
}
|
|
maskSize, bits := ipNet.Mask.Size()
|
|
if bits == 128 {
|
|
return nil, fmt.Errorf("%w: %q", ErrIPv6PrefixUnsupported, prefix)
|
|
}
|
|
if bits != 32 || maskSize < 0 || maskSize > 32 || ipNet.IP.To4() == nil {
|
|
return nil, fmt.Errorf("%w: %q", ErrInvalidPrefix, prefix)
|
|
}
|
|
|
|
// A /24 block is the output unit used by the original implementation. A
|
|
// /25-/32 prefix therefore has one containing block, while a /0 would need
|
|
// 2^24 entries and is rejected before allocation.
|
|
blocks := uint64(1)
|
|
if maskSize < 24 {
|
|
blocks = uint64(1) << uint(24-maskSize)
|
|
}
|
|
if blocks > uint64(maxEntries) {
|
|
return nil, fmt.Errorf("%w: %d entries (limit %d)", ErrPrefixListTooLarge, blocks, maxEntries)
|
|
}
|
|
|
|
start := binaryIPToInt(ipNet.IP.To4()) &^ uint32(255)
|
|
result := make([]string, 0, int(blocks))
|
|
for index := uint64(0); index < blocks; index++ {
|
|
// The arithmetic is bounded by 2^24 blocks, so this multiplication and
|
|
// addition cannot overflow uint32 for a valid IPv4 network.
|
|
value := start + uint32(index*256)
|
|
result = append(result, fmt.Sprintf("%d.%d.%d", byte(value>>24), byte(value>>16), byte(value>>8)))
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// 将IP地址转换为32位整数
|
|
func binaryIPToInt(ip net.IP) uint32 {
|
|
return (uint32(ip[0]) << 24) | (uint32(ip[1]) << 16) | (uint32(ip[2]) << 8) | uint32(ip[3])
|
|
}
|
|
|
|
// 将32位整数转换为IP地址
|
|
func intToBinaryIP(i uint32) net.IP {
|
|
return net.IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i))
|
|
}
|