This repository has no description
1// refuses outbound dials to special-purpose addresses, for anywhere
2// spindle fetches user-influenced urls (workflow caches, PDS blob
3// fetches)
4package netguard
5
6import (
7 "fmt"
8 "net"
9 "syscall"
10)
11
12// https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
13// https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
14// https://datatracker.ietf.org/doc/rfc6890/
15var BlockedRoutes = []string{
16 "0.0.0.0/8", // unspecified / "this network" addresses
17 "10.0.0.0/8", // private network
18 "100.64.0.0/10", // shared carrier-grade nat space
19 "127.0.0.0/8", // loopback
20 "169.254.0.0/16", // link-local / autoconfiguration
21 "172.16.0.0/12", // private network
22 "192.0.0.0/24", // ietf protocol assignments
23 "192.0.2.0/24", // documentation / examples
24 "192.88.99.0/24", // deprecated 6to4 relay anycast
25 "192.168.0.0/16", // private network
26 "198.18.0.0/15", // benchmarking / testing
27 "198.51.100.0/24", // documentation / examples
28 "203.0.113.0/24", // documentation / examples
29 "224.0.0.0/4", // multicast
30 "240.0.0.0/4", // reserved / future use, includes limited broadcast
31 "::/128", // unspecified address
32 "::1/128", // loopback
33 "::ffff:0:0/96", // ipv4-mapped addresses
34 "64:ff9b::/96", // ipv4/ipv6 translation prefix
35 "100::/64", // discard-only prefix
36 "2001::/23", // ietf protocol assignments
37 "2001:db8::/32", // documentation / examples
38 "2002::/16", // deprecated 6to4 addressing
39 "fc00::/7", // unique local addresses
40 "fe80::/10", // link-local unicast
41 "ff00::/8", // multicast
42}
43
44var BlockedNets = func() []*net.IPNet {
45 nets := make([]*net.IPNet, 0, len(BlockedRoutes))
46 for _, route := range BlockedRoutes {
47 _, ipnet, err := net.ParseCIDR(route)
48 if err != nil {
49 panic(fmt.Sprintf("parse blocked route %q: %v", route, err))
50 }
51 nets = append(nets, ipnet)
52 }
53 return nets
54}()
55
56// net.Dialer Control func rejecting blocked special-purpose addresses.
57// this should run after dns resolution, so it should cover any rebinding tricks
58func RefuseSpecialPurposeAddrs(network, address string, _ syscall.RawConn) error {
59 host, _, err := net.SplitHostPort(address)
60 if err != nil {
61 return fmt.Errorf("split dial address %q: %w", address, err)
62 }
63 ip := net.ParseIP(host)
64 if ip == nil {
65 return fmt.Errorf("refusing to dial non-IP address %q", host)
66 }
67 bits := 128
68 if ip4 := ip.To4(); ip4 != nil {
69 ip = ip4
70 bits = 32
71 }
72 for _, ipnet := range BlockedNets {
73 _, blockedBits := ipnet.Mask.Size()
74 if blockedBits != bits {
75 continue
76 }
77 if ipnet.Contains(ip) {
78 return fmt.Errorf("refusing to dial %s: %s is blocked", ip, ipnet)
79 }
80 }
81 return nil
82}