1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package httpclient
import (
"net/netip"
"syscall"
)
var (
// ipv6GlobalUnicast is the prefix set aside by IANA for global unicast assignments, i.e "the internet".
// https://www.iana.org/assignments/ipv6-unicast-address-assignments/ipv6-unicast-address-assignments.xhtml
ipv6GlobalUnicast = netip.MustParsePrefix("2000::/3")
// ipv6Reserved contains IPv6 reserved IP prefixes that fall within ipv6GlobalUnicast.
// https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
ipv6Reserved = [...]netip.Prefix{
netip.MustParsePrefix("2001::/23"), // IETF Protocol Assignments (RFC 2928)
netip.MustParsePrefix("2001:db8::/32"), // Documentation (RFC 3849)
netip.MustParsePrefix("2002::/16"), // 6to4 (RFC 3056)
netip.MustParsePrefix("2620:4f:8000::/48"), // Direct Delegation AS112 Service (RFC 7534)
}
// ipv4Reserved contains IPv4 reserved IP prefixes.
// https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
ipv4Reserved = [...]netip.Prefix{
netip.MustParsePrefix("0.0.0.0/8"), // Current network
netip.MustParsePrefix("10.0.0.0/8"), // Private
netip.MustParsePrefix("100.64.0.0/10"), // RFC6598
netip.MustParsePrefix("127.0.0.0/8"), // Loopback
netip.MustParsePrefix("169.254.0.0/16"), // Link-local
netip.MustParsePrefix("172.16.0.0/12"), // Private
netip.MustParsePrefix("192.0.0.0/24"), // RFC6890
netip.MustParsePrefix("192.0.2.0/24"), // Test, doc, examples
netip.MustParsePrefix("192.31.196.0/24"), // AS112-v4, RFC 7535
netip.MustParsePrefix("192.52.193.0/24"), // AMT, RFC 7450
netip.MustParsePrefix("192.88.99.0/24"), // IPv6 to IPv4 relay
netip.MustParsePrefix("192.168.0.0/16"), // Private
netip.MustParsePrefix("192.175.48.0/24"), // Direct Delegation AS112 Service, RFC 7534
netip.MustParsePrefix("198.18.0.0/15"), // Benchmarking tests
netip.MustParsePrefix("198.51.100.0/24"), // Test, doc, examples
netip.MustParsePrefix("203.0.113.0/24"), // Test, doc, examples
netip.MustParsePrefix("224.0.0.0/4"), // Multicast
netip.MustParsePrefix("240.0.0.0/4"), // Reserved (includes broadcast / 255.255.255.255)
}
)
type Sanitizer struct {
Allow []netip.Prefix
Block []netip.Prefix
}
// Sanitize implements the required net.Dialer.Control function signature.
func (s *Sanitizer) Sanitize(ntwrk, addr string, _ syscall.RawConn) error {
// Parse IP+port from addr
ipport, err := netip.ParseAddrPort(addr)
if err != nil {
return err
}
// Ensure valid network.
const (
tcp4 = "tcp4"
tcp6 = "tcp6"
)
if !(ntwrk == tcp4 || ntwrk == tcp6) {
return ErrInvalidNetwork
}
// Separate the IP.
ip := ipport.Addr()
// Check if this IP is explicitly allowed.
for i := 0; i < len(s.Allow); i++ {
if s.Allow[i].Contains(ip) {
return nil
}
}
// Check if this IP is explicitly blocked.
for i := 0; i < len(s.Block); i++ {
if s.Block[i].Contains(ip) {
return ErrReservedAddr
}
}
// Validate this is a safe IP.
if !SafeIP(ip) {
return ErrReservedAddr
}
return nil
}
// SafeIP returns whether ip is an IPv4/6
// address in a non-reserved, public range.
func SafeIP(ip netip.Addr) bool {
switch {
// IPv4: check if IPv4 in reserved nets
case ip.Is4():
for _, reserved := range ipv4Reserved {
if reserved.Contains(ip) {
return false
}
}
return true
// IPv6: check if IP in IPv6 reserved nets
case ip.Is6():
if !ipv6GlobalUnicast.Contains(ip) {
// Address is not globally routeable,
// ie., not "on the internet".
return false
}
for _, reserved := range ipv6Reserved {
if reserved.Contains(ip) {
// Address is globally routeable
// but falls in a reserved range.
return false
}
}
return true
// Assume malicious by default
default:
return false
}
}
|