package netguard
import (
"errors"
"fmt"
"net"
"net/netip"
"syscall"
)
var ErrPrivateAddress = errors.New("dial to private/loopback address blocked")
func IsPrivateIP(ip net.IP) bool {
return ip.IsLoopback() || ip.IsUnspecified() || ip.IsPrivate() || ip.IsLinkLocalUnicast() || ip.IsLinkLocalMulticast()
}
func DialControl(allowed ...netip.Prefix) func(network, address string, c syscall.RawConn) error {
return func(_, address string, _ syscall.RawConn) error {
ap, err := netip.ParseAddrPort(address)
if err != nil {
return fmt.Errorf("%w: unparseable address %q: %w", ErrPrivateAddress, address, err)
}
addr := ap.Addr().Unmap()
if !IsPrivateIP(addr.AsSlice()) {
return nil
}
for _, p := range allowed {
if p.Contains(addr) {
return nil
}
}
return fmt.Errorf("%w: %s", ErrPrivateAddress, address)
}
}