66 lines
2 KiB
Go
66 lines
2 KiB
Go
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
// Package netutil contains stdlib address helpers matching the Python oracle.
|
|
package netutil
|
|
|
|
import (
|
|
"net/netip"
|
|
"strings"
|
|
)
|
|
|
|
var carrierGradeNAT = netip.MustParsePrefix("100.64.0.0/10")
|
|
|
|
// ParseAddr accepts brackets and IPv6 zone suffixes used by ss.
|
|
func ParseAddr(value string) (netip.Addr, bool) {
|
|
value = strings.Trim(strings.TrimSpace(value), "[]")
|
|
if index := strings.IndexByte(value, '%'); index >= 0 {
|
|
value = value[:index]
|
|
}
|
|
address, err := netip.ParseAddr(value)
|
|
return address, err == nil
|
|
}
|
|
|
|
// IsPublicIP reports globally routable addresses and excludes CGNAT.
|
|
func IsPublicIP(value string) bool {
|
|
address, ok := ParseAddr(value)
|
|
// netip's GlobalUnicast includes CGNAT, so explicitly exclude 100.64/10
|
|
// to match Python ipaddress.is_global and avoid treating carrier-local
|
|
// traffic as public C2 egress.
|
|
if !ok || !address.IsGlobalUnicast() || address.IsPrivate() ||
|
|
address.IsLoopback() || address.IsLinkLocalUnicast() {
|
|
return false
|
|
}
|
|
return !carrierGradeNAT.Contains(address)
|
|
}
|
|
|
|
// IPInCIDRs checks an address against valid same-family trust prefixes.
|
|
func IPInCIDRs(value string, cidrs []string) bool {
|
|
address, ok := ParseAddr(value)
|
|
if !ok {
|
|
return false
|
|
}
|
|
for _, raw := range cidrs {
|
|
// Invalid operator entries are ignored just like Python Config. A typo
|
|
// must not crash or disable the detector sweep.
|
|
prefix, err := netip.ParsePrefix(strings.TrimSpace(raw))
|
|
if err == nil && prefix.Addr().BitLen() == address.BitLen() && prefix.Contains(address) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// SplitHostPort splits ss-style IPv4, IPv6, and zone-qualified endpoints.
|
|
func SplitHostPort(endpoint string) (string, string) {
|
|
endpoint = strings.TrimSpace(endpoint)
|
|
if strings.HasPrefix(endpoint, "[") {
|
|
if end := strings.Index(endpoint, "]"); end >= 0 {
|
|
return endpoint[1:end], strings.TrimPrefix(endpoint[end+1:], ":")
|
|
}
|
|
}
|
|
index := strings.LastIndex(endpoint, ":")
|
|
if index < 0 {
|
|
return "", endpoint
|
|
}
|
|
return endpoint[:index], endpoint[index+1:]
|
|
}
|